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.
Files changed (144) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +20 -30
  3. package/dist/src/access-control.js +1 -2
  4. package/dist/src/access-dashboard.js +2 -3
  5. package/dist/src/account-access-policy.js +54 -0
  6. package/dist/src/authorized-workspace-roots.js +0 -1
  7. package/dist/src/batch-read-policy.js +30 -0
  8. package/dist/src/cli/instances.js +6 -175
  9. package/dist/src/cli/oauth.js +0 -1
  10. package/dist/src/cli/starmux-status.js +10 -14
  11. package/dist/src/cli/starmux-supervisor.js +0 -1
  12. package/dist/src/cli/starmux.js +13 -25
  13. package/dist/src/config-schemas.js +0 -9
  14. package/dist/src/configured-mcp-upstreams.js +0 -1
  15. package/dist/src/contracts.js +0 -1
  16. package/dist/src/current-permission-probe.js +0 -1
  17. package/dist/src/dc-workspace-http.js +57 -33
  18. package/dist/src/deny-dashboard.js +2 -3
  19. package/dist/src/desktop-commander-facade.js +4 -4
  20. package/dist/src/explicit-deny-rules.js +2 -3
  21. package/dist/src/global-core-control.js +2 -3
  22. package/dist/src/home-write-sandbox.js +0 -1
  23. package/dist/src/http-host-policy.js +20 -0
  24. package/dist/src/instance-runtime.js +3 -3
  25. package/dist/src/instance-store.js +28 -22
  26. package/dist/src/instance-worker.js +2 -3
  27. package/dist/src/invocation.js +1 -2
  28. package/dist/src/local-desktop-read-probe.js +5 -88
  29. package/dist/src/managed-workspace-policy.js +38 -4
  30. package/dist/src/mcp-results.js +0 -1
  31. package/dist/src/mcp-upstream-router.js +0 -1
  32. package/dist/src/oauth-keycloak.js +0 -1
  33. package/dist/src/oauth-resource.js +0 -1
  34. package/dist/src/ops-dashboard.js +2 -179
  35. package/dist/src/permissions-dashboard.js +3 -4
  36. package/dist/src/platform.js +3 -4
  37. package/dist/src/privacy-diagnostics.js +0 -1
  38. package/dist/src/private-mcp-activity.js +0 -1
  39. package/dist/src/private-store-lock.js +0 -1
  40. package/dist/src/project-dev-presets.js +0 -1
  41. package/dist/src/project-git-workflow.js +0 -1
  42. package/dist/src/project-workspace-policy.js +10 -6
  43. package/dist/src/runtime-identity.js +0 -14
  44. package/dist/src/runtime-paths.js +0 -3
  45. package/dist/src/tool-dispatch.js +45 -46
  46. package/dist/src/validation.js +0 -1
  47. package/dist/src/workspace-grants.js +1 -2
  48. package/package.json +13 -17
  49. package/scripts/prepare.mjs +1 -1
  50. package/CONTRIBUTING.md +0 -18
  51. package/SECURITY.md +0 -7
  52. package/app/README.md +0 -29
  53. package/dist/src/access-control.js.map +0 -1
  54. package/dist/src/access-dashboard.js.map +0 -1
  55. package/dist/src/authorized-workspace-roots.js.map +0 -1
  56. package/dist/src/cli/access.js +0 -53
  57. package/dist/src/cli/access.js.map +0 -1
  58. package/dist/src/cli/community-remote-revoke.js +0 -32
  59. package/dist/src/cli/community-remote-revoke.js.map +0 -1
  60. package/dist/src/cli/instances.js.map +0 -1
  61. package/dist/src/cli/live-home-settings.js +0 -35
  62. package/dist/src/cli/live-home-settings.js.map +0 -1
  63. package/dist/src/cli/mac-mcp-gateway.js +0 -257
  64. package/dist/src/cli/mac-mcp-gateway.js.map +0 -1
  65. package/dist/src/cli/oauth.js.map +0 -1
  66. package/dist/src/cli/permissions-owner.js +0 -27
  67. package/dist/src/cli/permissions-owner.js.map +0 -1
  68. package/dist/src/cli/service-setup.js +0 -26
  69. package/dist/src/cli/service-setup.js.map +0 -1
  70. package/dist/src/cli/setup-wizard.js +0 -149
  71. package/dist/src/cli/setup-wizard.js.map +0 -1
  72. package/dist/src/cli/starmux-status.js.map +0 -1
  73. package/dist/src/cli/starmux-supervisor.js.map +0 -1
  74. package/dist/src/cli/starmux.js.map +0 -1
  75. package/dist/src/cli/verify-setup.js +0 -53
  76. package/dist/src/cli/verify-setup.js.map +0 -1
  77. package/dist/src/community-remote-revocations.js +0 -135
  78. package/dist/src/community-remote-revocations.js.map +0 -1
  79. package/dist/src/config-schemas.js.map +0 -1
  80. package/dist/src/configured-mcp-upstreams.js.map +0 -1
  81. package/dist/src/contracts.js.map +0 -1
  82. package/dist/src/current-permission-probe.js.map +0 -1
  83. package/dist/src/dc-workspace-http.js.map +0 -1
  84. package/dist/src/deny-dashboard.js.map +0 -1
  85. package/dist/src/desktop-commander-facade.js.map +0 -1
  86. package/dist/src/explicit-deny-rules.js.map +0 -1
  87. package/dist/src/gateway-status-format.js +0 -66
  88. package/dist/src/gateway-status-format.js.map +0 -1
  89. package/dist/src/global-core-control.js.map +0 -1
  90. package/dist/src/home-write-sandbox.js.map +0 -1
  91. package/dist/src/instance-migration.js +0 -138
  92. package/dist/src/instance-migration.js.map +0 -1
  93. package/dist/src/instance-runtime.js.map +0 -1
  94. package/dist/src/instance-store.js.map +0 -1
  95. package/dist/src/instance-worker.js.map +0 -1
  96. package/dist/src/invocation.js.map +0 -1
  97. package/dist/src/live-profile-home-settings.js +0 -80
  98. package/dist/src/live-profile-home-settings.js.map +0 -1
  99. package/dist/src/local-desktop-read-probe.js.map +0 -1
  100. package/dist/src/managed-workspace-policy.js.map +0 -1
  101. package/dist/src/mcp-results.js.map +0 -1
  102. package/dist/src/mcp-upstream-router.js.map +0 -1
  103. package/dist/src/oauth-keycloak.js.map +0 -1
  104. package/dist/src/oauth-resource.js.map +0 -1
  105. package/dist/src/operator-cli.js +0 -559
  106. package/dist/src/operator-cli.js.map +0 -1
  107. package/dist/src/ops-dashboard.js.map +0 -1
  108. package/dist/src/permissions-dashboard.js.map +0 -1
  109. package/dist/src/platform.js.map +0 -1
  110. package/dist/src/privacy-diagnostics.js.map +0 -1
  111. package/dist/src/private-mcp-activity.js.map +0 -1
  112. package/dist/src/private-store-lock.js.map +0 -1
  113. package/dist/src/project-dev-presets.js.map +0 -1
  114. package/dist/src/project-git-workflow.js.map +0 -1
  115. package/dist/src/project-workspace-policy.js.map +0 -1
  116. package/dist/src/read-scope.js +0 -44
  117. package/dist/src/read-scope.js.map +0 -1
  118. package/dist/src/release1-onboarding.js +0 -120
  119. package/dist/src/release1-onboarding.js.map +0 -1
  120. package/dist/src/release1-service.js +0 -131
  121. package/dist/src/release1-service.js.map +0 -1
  122. package/dist/src/remote-auth.js +0 -47
  123. package/dist/src/remote-auth.js.map +0 -1
  124. package/dist/src/runtime-identity.js.map +0 -1
  125. package/dist/src/runtime-paths.js.map +0 -1
  126. package/dist/src/starmux-migration.js +0 -172
  127. package/dist/src/starmux-migration.js.map +0 -1
  128. package/dist/src/tool-dispatch.js.map +0 -1
  129. package/dist/src/validation.js.map +0 -1
  130. package/dist/src/workspace-grants.js.map +0 -1
  131. package/docs/ACCESS_CONTROL.md +0 -51
  132. package/docs/CLIENTS.md +0 -22
  133. package/docs/CONNECT_CHATGPT.md +0 -148
  134. package/docs/INSTALL.md +0 -62
  135. package/docs/INSTANCES.md +0 -83
  136. package/docs/OAUTH.md +0 -116
  137. package/docs/PLATFORMS.md +0 -29
  138. package/docs/README.md +0 -16
  139. package/docs/REMOTE_SETUP.md +0 -280
  140. package/docs/SECURITY.md +0 -15
  141. package/docs/assets/starmux-app-en.png +0 -0
  142. package/docs/assets/starmux-architecture.svg +0 -315
  143. package/src/dc-workspace-http.mjs +0 -3
  144. 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":"AAEA,OAAO,EAAE,mBAAmB,EAAC,wBAAwB,EAAC,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC7F,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,2EAA2E;AAC3E,gFAAgF;AAChF,MAAM,UAAU,oBAAoB,CAAC,QAAwB,EAAE,KAAgB,EAAE,OAAsB;IACrG,MAAM,OAAO,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACnD,OAAO,KAAK,EAAE,MAAsB,EAAE,OAAuB,EAA2B,EAAE;QACxF,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,CAAC,EAAc,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACtE,IAAG,IAAI,KAAG,cAAc,EAAC,CAAC;YACxB,KAAK,CAAC,EAAC,KAAK,EAAC,wBAAwB,EAAC,SAAS,EAAC,EAAE,EAAC,CAAC,CAAC;YACrD,OAAO,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,IAAI,CAAC,SAAS,CAAC;4BAChD,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,2BAA2B;4BAC/C,QAAQ,EAAC,mBAAmB,EAAC,KAAK,EAAC,QAAQ,CAAC,eAAe,CAAC,MAAM;4BAClE,MAAM,EAAC,OAAO,CAAC,cAAc,EAAE,MAAM,IAAE,CAAC,EAAC,EAAE,EAAC,SAAS,EAAC,SAAS,EAAC,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAC,CAAC;4BACjG,aAAa,EAAC,OAAO,CAAC,eAAe,EAAE,IAAI,IAAE,eAAe;4BAC5D,iBAAiB,EAAC,OAAO,CAAC,eAAe,EAAE,iBAAiB,IAAE,eAAe;4BAC7E,YAAY,EAAC,OAAO,CAAC,iBAAiB,KAAG,IAAI;4BAC7C,OAAO,EAAC,EAAE;yBACX,CAAC,EAAC,CAAC,EAAC,CAAC;QACR,CAAC;QACD,sEAAsE;QACtE,IAAG,IAAI,KAAG,YAAY,IAAE,OAAO,CAAC,eAAe,IAAE,CAAC,OAAO,CAAC,iBAAiB,EAAC,CAAC;YAC3E,MAAM,MAAM,GAAC,OAAO,CAAC,eAAe,CAAC;YACrC,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,CAAC,CAAC;YAClD,OAAO,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,IAAI,CAAC,SAAS,CAAC;4BAChD,MAAM,EAAC,iBAAiB,EAAC,UAAU,EAAC,6BAA6B;4BACjE,WAAW,EAAC,MAAM,CAAC,IAAI,EAAC,aAAa,EAAC,MAAM,CAAC,IAAI;4BACjD,iBAAiB,EAAC,MAAM,CAAC,iBAAiB;4BAC1C,aAAa,EAAC,gEAAgE;4BAC9E,oBAAoB,EAAC,YAAY;4BACjC,eAAe,EAAC,QAAQ,CAAC,eAAe,CAAC,MAAM;4BAC/C,eAAe,EAAC,MAAM,CAAC,cAAc,CAAA,CAAC,CAAA,cAAc,CAAA,CAAC,CAAA,IAAI;4BACzD,IAAI,EAAC,sEAAsE;yBAC5E,EAAC,IAAI,EAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC;QACf,CAAC;QACD,IAAG,IAAI,KAAG,kBAAkB,IAAE,OAAO,CAAC,eAAe,IAAE,CAAC,OAAO,CAAC,iBAAiB,EAAC,CAAC;YACjF,KAAK,CAAC,EAAC,KAAK,EAAC,gCAAgC,EAAC,SAAS,EAAC,EAAE,EAAC,CAAC,CAAC;YAC7D,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAC7C,0FAA0F,EAAC,CAAC,EAAC,CAAC;QAClG,CAAC;QACD,IAAG,IAAI,KAAG,oBAAoB,IAAE,OAAO,CAAC,eAAe,EAAE,gBAAgB,EAAC,CAAC;YACzE,IAAG,CAAC;gBACF,IAAG,CAAC,IAAI,IAAE,OAAO,IAAI,CAAC,IAAI,KAAG,QAAQ;oBAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,CAAC,MAAM,EAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBACzD,MAAM,KAAK,CAAC,6BAA6B,CAAC,CAAC;gBAC7C,MAAM,UAAU,GAAC,MAAM,OAAO,CAAC,eAAe,CAAC,gBAAgB,CAC7D,IAAI,CAAC,IAAI,EAAC,IAAI,CAAC,KAAK,IAAE,CAAC,CAAC,CAAC;gBAC3B,MAAM,MAAM,GAAC,MAAM,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC,eAAe,EAAC,MAAM,EACrE,EAAC,OAAO,EAAC,oBAAoB,EAAC,CAAC,CAAC,CAAC;gBACnC,KAAK,CAAC,EAAC,KAAK,EAAC,8BAA8B,EAAC,SAAS,EAAC,EAAE;oBACtD,OAAO,EAAC,MAAM,CAAC,OAAO,KAAG,IAAI,EAAC,CAAC,CAAC;gBAClC,OAAO,MAAM,CAAC;YAChB,CAAC;YAAA,OAAM,KAAK,EAAC,CAAC;gBACZ,KAAK,CAAC,EAAC,KAAK,EAAC,2BAA2B,EAAC,SAAS,EAAC,EAAE;oBACnD,MAAM,EAAC,SAAS,CAAC,KAAK,CAAC,IAAE,kBAAkB,EAAC,CAAC,CAAC;gBAChD,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,oBAAoB;gCAClE,0DAA0D,EAAC,CAAC,EAAC,CAAC;YAClE,CAAC;QACH,CAAC;QACD,IAAG,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAC,CAAC;YAC5C,KAAK,CAAC,EAAC,KAAK,EAAC,sBAAsB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;YAC7D,IAAG,CAAC;gBACF,MAAM,MAAM,GAAC,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAC,IAAI,CAAC,CAAC;gBACzD,KAAK,CAAC,EAAC,KAAK,EAAC,uBAAuB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;gBAC9D,OAAO,MAAM,CAAC;YAChB,CAAC;YAAA,OAAM,KAAK,EAAC,CAAC;gBACZ,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI;oBACvD,MAAM,EAAC,SAAS,CAAC,KAAK,CAAC,IAAE,qBAAqB,EAAC,CAAC,CAAC;gBACnD,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,qBAAqB,EAAC,CAAC,EAAC,CAAC;YAC3E,CAAC;QACH,CAAC;QACD,IAAG,OAAO,CAAC,cAAc,EAAE,QAAQ,CAAC,IAAI,CAAC;YACtC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAG,SAAS,EAAC,CAAC;YACpD,IAAI,MAAM,CAAC;YACX,IAAG,CAAC;gBAAA,MAAM,GAAC,MAAM,OAAO,CAAC,cAAc,CAAC,kBAAkB,CAAC,IAAI,EAAC,IAAI,CAAC,CAAC;YAAA,CAAC;YACvE,OAAM,KAAK,EAAC,CAAC;gBACX,KAAK,CAAC,EAAC,KAAK,EAAC,kBAAkB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI;oBACpD,MAAM,EAAC,SAAS,CAAC,KAAK,CAAC,IAAE,cAAc,EAAC,CAAC,CAAC;gBAC5C,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,cAAc,EAAC,CAAC,EAAC,CAAC;YACpE,CAAC;YACD,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;YAC5D,IAAG,CAAC;gBACF,MAAM,MAAM,GAAC,MAAM,OAAO,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;gBACxD,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI;oBACvD,OAAO,EAAC,MAAM,CAAC,OAAO,KAAG,IAAI,EAAC,CAAC,CAAC;gBAClC,OAAO,MAAM,CAAC;YAChB,CAAC;YAAA,OAAM,KAAK,EAAC,CAAC;gBACZ,qEAAqE;gBACrE,MAAM,IAAI,GAAC,SAAS,CAAC,KAAK,CAAC,KAAG,yBAAyB,CAAA,CAAC;oBACtD,yBAAyB,CAAA,CAAC,CAAA,sBAAsB,CAAC;gBACnD,KAAK,CAAC,EAAC,KAAK,EAAC,2BAA2B,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,CAAC,CAAC;gBAC9E,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,EAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QACD,uEAAuE;QACvE,oEAAoE;QACpE,sEAAsE;QACtE,mEAAmE;QACnE,IAAG,OAAO,CAAC,iBAAiB;YACzB,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAC,IAAI,KAAG,IAAI,CAAC,EAAC,CAAC;YACxD,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;YAC5D,IAAG,CAAC;gBACF,MAAM,MAAM,GAAC,MAAM,OAAO,CAAC,mBAAmB,CAAC,EAAC,IAAI,EAAC,SAAS,EAAC,IAAI,EAAC,CAAC,CAAC,CAAC;gBACvE,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI;oBACvD,OAAO,EAAC,MAAM,CAAC,OAAO,KAAG,IAAI,EAAC,CAAC,CAAC;gBAClC,OAAO,MAAM,CAAC;YAChB,CAAC;YAAA,OAAM,KAAK,EAAC,CAAC;gBACZ,MAAM,MAAM,GAAC,SAAS,CAAC,KAAK,CAAC,KAAG,yBAAyB,CAAA,CAAC;oBACxD,yBAAyB,CAAA,CAAC,CAAA,sBAAsB,CAAC;gBACnD,KAAK,CAAC,EAAC,KAAK,EAAC,2BAA2B,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,MAAM,EAAC,CAAC,CAAC;gBACzE,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,CAAC,EAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QACD,IAAG,OAAO,CAAC,eAAe,EAAC,CAAC;YAC1B,IAAI,UAAU,CAAC;YACf,IAAG,CAAC;gBAAA,UAAU,GAAC,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,EAAC,IAAI,CAAC,CAAC;YAAA,CAAC;YACjE,OAAM,KAAK,EAAC,CAAC;gBACX,KAAK,CAAC,EAAC,KAAK,EAAC,kBAAkB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI;oBAC/C,MAAM,EAAC,SAAS,CAAC,KAAK,CAAC,IAAE,cAAc,EAAC,CAAC,CAAC;gBACjD,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAC7C,OAAO,CAAC,eAAe,CAAC,cAAc,CAAA,CAAC;gCACrC,8EAA8E,CAAA,CAAC;gCAC/E,kBAAkB,EAAC,CAAC,EAAC,CAAC;YAC5B,CAAC;YACD,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;YAC5D,IAAG,CAAC;gBACF,MAAM,MAAM,GAAC,MAAM,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC,IAAI,EAAC,MAAM,EAAC,IAAI,CAAC,CAAC,CAAC;gBACpE,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI;oBAClD,YAAY,EAAC,IAAI,KAAG,eAAe,CAAA,CAAC,CAAA,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAE,EAAE,CAAC,CAAC,CAAA,CAAC,CAAA,SAAS;oBACzF,OAAO,EAAC,MAAM,CAAC,OAAO,KAAG,IAAI,EAAC,CAAC,CAAC;gBACvC,OAAO,MAAM,CAAC;YAChB,CAAC;YAAA,MAAK,CAAC;gBACL,KAAK,CAAC,EAAC,KAAK,EAAC,2BAA2B,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;gBAClE,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,sBAAsB,EAAC,CAAC,EAAC,CAAC;YAC5E,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAC,CAAC,IAAI,KAAG,WAAW;YAC/B,CAAC,gBAAgB,EAAC,sBAAsB,EAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjF,IAAI,CAAC,MAAM,KAAG,CAAC,IAAE,IAAI,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC;YAClC,CAAC,IAAI,KAAG,YAAY,IAAE,IAAI,CAAC,IAAI,KAAG,sBAAsB;gBACvD,OAAO,IAAI,CAAC,OAAO,KAAG,QAAQ,IAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAE,GAAG;gBACpE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,MAAM,EAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/C,CAAC,IAAI,KAAG,eAAe,IAAE,IAAI,CAAC,OAAO,KAAG,mBAAmB;gBAC1D,IAAI,CAAC,UAAU,KAAG,IAAI;gBACtB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA,EAAE,CAAA,CAAC,SAAS,EAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,IAAG,CAAC,OAAO,EAAC,CAAC;YACX,KAAK,CAAC,EAAC,KAAK,EAAC,kBAAkB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;YACzD,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,4BAA4B,EAAC,CAAC,EAAC,CAAC;QAClF,CAAC;QACD,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;QAC5D,IAAI,CAAC;YACH,IAAI,QAAQ,CAAC;YACb,IAAG,IAAI,KAAG,WAAW,IAAE,IAAI,CAAC,IAAI,KAAG,WAAW;gBAC5C,QAAQ,GAAC,MAAM,QAAQ,CAAC,gBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;iBAC1D,IAAG,IAAI,KAAG,WAAW;gBACxB,QAAQ,GAAC,MAAM,QAAQ,CAAC,uBAAwB,EAAE,CAAC;iBAChD,IAAG,IAAI,KAAG,YAAY;gBACzB,QAAQ,GAAC,MAAM,QAAQ,CAAC,uBAAwB,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;;gBAEvE,QAAQ,GAAC,MAAM,QAAQ,CAAC,sBAAuB,EAAE,CAAC;YACpD,KAAK,CAAC,EAAC,KAAK,EAAC,qBAAqB,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI;gBAClD,OAAO,EAAC,QAAQ,CAAC,OAAO,KAAG,IAAI,EAAC,CAAC,CAAC;YACzC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAA,MAAK,CAAC;YACL,KAAK,CAAC,EAAC,KAAK,EAAC,2BAA2B,EAAC,SAAS,EAAC,EAAE,EAAC,IAAI,EAAC,IAAI,EAAC,CAAC,CAAC;YAClE,OAAO,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,CAAC,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,iBAAiB,EAAC,CAAC,EAAC,CAAC;QACvE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","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":"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","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"]}
@@ -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)
@@ -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.