@hazem3500/palette 0.0.0-stage → 0.9.106

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 (244) hide show
  1. package/LICENSE +21 -0
  2. package/NODE.md +166 -0
  3. package/NOTICE.md +45 -0
  4. package/README.md +912 -2
  5. package/cli/agent.mjs +752 -0
  6. package/cli/config.mjs +34 -0
  7. package/cli/design-edit.mjs +336 -0
  8. package/cli/index.mjs +405 -0
  9. package/cli/project.mjs +151 -0
  10. package/cli/snapshots.mjs +61 -0
  11. package/cli/workflow.mjs +42 -0
  12. package/dist/MorphingMenu.d.ts +38 -0
  13. package/dist/PointerIcon.d.ts +2 -0
  14. package/dist/SmileIcon.d.ts +2 -0
  15. package/dist/THIRD_PARTY_NOTICES.txt +387 -0
  16. package/dist/accent-picker-TTIZ65XF.js +1 -0
  17. package/dist/accent-picker.d.ts +4 -0
  18. package/dist/accent.d.ts +2 -0
  19. package/dist/account-tokens-PINQNSNE.js +1 -0
  20. package/dist/account-tokens.d.ts +25 -0
  21. package/dist/account-usage.d.ts +2 -0
  22. package/dist/action-menu.d.ts +10 -0
  23. package/dist/adapters/class-theme.d.ts +25 -0
  24. package/dist/adapters/class-theme.js +1 -0
  25. package/dist/adapters/css-variables.d.ts +15 -0
  26. package/dist/adapters/css-variables.js +1 -0
  27. package/dist/adapters/react-fiber.d.ts +56 -0
  28. package/dist/adapters/react-fiber.js +1 -0
  29. package/dist/adapters/react-preview.d.ts +17 -0
  30. package/dist/adapters/react-preview.js +1 -0
  31. package/dist/adapters/tailwind.d.ts +90 -0
  32. package/dist/adapters/tailwind.js +1 -0
  33. package/dist/adapters/variable-preview.d.ts +15 -0
  34. package/dist/agent-prompt.d.ts +10 -0
  35. package/dist/agent-prompt.js +1 -0
  36. package/dist/anchors.d.ts +52 -0
  37. package/dist/api.d.ts +40 -0
  38. package/dist/approved-sites.d.ts +12 -0
  39. package/dist/card-motion.d.ts +8 -0
  40. package/dist/chunk-2BS2OWWH.js +1 -0
  41. package/dist/chunk-2J3IID4N.js +1 -0
  42. package/dist/chunk-3BVM3I52.js +1 -0
  43. package/dist/chunk-4FX5IS2U.js +1 -0
  44. package/dist/chunk-4KEVPMJJ.js +1 -0
  45. package/dist/chunk-4XQD75A2.js +1 -0
  46. package/dist/chunk-57MG3M5P.js +1 -0
  47. package/dist/chunk-5OVDW2RU.js +1 -0
  48. package/dist/chunk-6CIC7VU7.js +1 -0
  49. package/dist/chunk-6HM4JQLV.js +1 -0
  50. package/dist/chunk-AYGLN3MI.js +1 -0
  51. package/dist/chunk-DLC3CRFV.js +1 -0
  52. package/dist/chunk-DVYSFT6O.js +1 -0
  53. package/dist/chunk-DZRM3YUQ.js +1 -0
  54. package/dist/chunk-FD4GACV2.js +1 -0
  55. package/dist/chunk-GGU2XLWE.js +1 -0
  56. package/dist/chunk-IQOPM3JF.js +1 -0
  57. package/dist/chunk-LMXFSXO6.js +1 -0
  58. package/dist/chunk-QFDNGHSH.js +1 -0
  59. package/dist/chunk-RBHY7UDS.js +1 -0
  60. package/dist/chunk-UEZCLURX.js +1 -0
  61. package/dist/chunk-XOMOONGK.js +1 -0
  62. package/dist/chunk-XP22SMEQ.js +1 -0
  63. package/dist/color-editor-ATWWSENS.js +1 -0
  64. package/dist/color-editor.d.ts +37 -0
  65. package/dist/component-adapters.d.ts +3 -0
  66. package/dist/config.d.ts +13 -0
  67. package/dist/connect-project.d.ts +14 -0
  68. package/dist/connection-issue.d.ts +7 -0
  69. package/dist/css-value-parts.d.ts +14 -0
  70. package/dist/custom-field.d.ts +12 -0
  71. package/dist/design-commands.d.ts +46 -0
  72. package/dist/design-comment-RXUCDTYA.js +1 -0
  73. package/dist/design-comment.d.ts +98 -0
  74. package/dist/design-export-BNNHI4JP.js +1 -0
  75. package/dist/design-export.d.ts +87 -0
  76. package/dist/design-geometry-DPMHGQEK.js +1 -0
  77. package/dist/design-geometry.d.ts +61 -0
  78. package/dist/design-keyboard.d.ts +27 -0
  79. package/dist/design-mode-styles.d.ts +1 -0
  80. package/dist/design-mode.d.ts +48 -0
  81. package/dist/design-mode.js +1 -0
  82. package/dist/design-move.d.ts +75 -0
  83. package/dist/design-panel-HTN6LR3V.js +1 -0
  84. package/dist/design-panel-styles.d.ts +1 -0
  85. package/dist/design-panel.d.ts +42 -0
  86. package/dist/design-popup.d.ts +11 -0
  87. package/dist/design-preview.d.ts +42 -0
  88. package/dist/design-system.d.ts +314 -0
  89. package/dist/design-target.d.ts +5 -0
  90. package/dist/design-tokens.d.ts +93 -0
  91. package/dist/dom.d.ts +35 -0
  92. package/dist/drawer-expand-motion.d.ts +46 -0
  93. package/dist/drawer-presence.d.ts +4 -0
  94. package/dist/edge-sidebar.d.ts +39 -0
  95. package/dist/element-label.d.ts +7 -0
  96. package/dist/emoji-history.d.ts +8 -0
  97. package/dist/emoji-sequence.d.ts +1 -0
  98. package/dist/emoji.d.ts +1 -0
  99. package/dist/field-controls-4ROTPXMO.js +1 -0
  100. package/dist/field-controls.d.ts +1 -0
  101. package/dist/field-label.d.ts +8 -0
  102. package/dist/floating-drag.d.ts +9 -0
  103. package/dist/frame-iframe-WBEP3R6S.js +1 -0
  104. package/dist/frame-iframe.d.ts +38 -0
  105. package/dist/frame-portals.d.ts +14 -0
  106. package/dist/frame-size-MRCN6XYD.js +1 -0
  107. package/dist/frame-size.d.ts +105 -0
  108. package/dist/frame-target.d.ts +65 -0
  109. package/dist/frame-viewport.d.ts +8 -0
  110. package/dist/highlight.d.ts +11 -0
  111. package/dist/host-motion.d.ts +6 -0
  112. package/dist/icon-markup.d.ts +4 -0
  113. package/dist/index.d.ts +10 -0
  114. package/dist/index.js +1 -0
  115. package/dist/inline-text-editor-AMFRKW5Z.js +1 -0
  116. package/dist/inline-text-editor.d.ts +66 -0
  117. package/dist/kbd.d.ts +17 -0
  118. package/dist/lazy-accent-picker.d.ts +4 -0
  119. package/dist/lazy-motion.d.ts +17 -0
  120. package/dist/lazy-toolbar.d.ts +22 -0
  121. package/dist/live-colors-GWOVVQSO.js +1 -0
  122. package/dist/live-colors.d.ts +13 -0
  123. package/dist/message-links.d.ts +2 -0
  124. package/dist/morphing-menu-styles.d.ts +1 -0
  125. package/dist/motion-animate-J4PEZPLG.js +1 -0
  126. package/dist/motion-animate.d.ts +1 -0
  127. package/dist/node.mjs +3731 -0
  128. package/dist/onboarding-BVTCFEHL.js +1 -0
  129. package/dist/onboarding.d.ts +6 -0
  130. package/dist/open-state.d.ts +6 -0
  131. package/dist/optimistic.d.ts +26 -0
  132. package/dist/page-label.d.ts +2 -0
  133. package/dist/page.d.ts +2 -0
  134. package/dist/picker-CMP4UZRF.js +1 -0
  135. package/dist/pin-direction.d.ts +12 -0
  136. package/dist/pin-groups-YAYFPJYA.js +1 -0
  137. package/dist/pin-groups.d.ts +28 -0
  138. package/dist/pin-move-PG54WWPU.js +1 -0
  139. package/dist/pin-move.d.ts +22 -0
  140. package/dist/pin-stacks.d.ts +22 -0
  141. package/dist/polling.d.ts +10 -0
  142. package/dist/postgres/001_initial.sql +128 -0
  143. package/dist/postgres/002_thread_tags.sql +1 -0
  144. package/dist/postgres/003_project_webhooks.sql +4 -0
  145. package/dist/postgres/004_personal_access_tokens.sql +25 -0
  146. package/dist/postgres/005_snapshots.sql +23 -0
  147. package/dist/postgres/006_thread_scope.sql +1 -0
  148. package/dist/postgres/007_session_origin.sql +4 -0
  149. package/dist/project-management.d.ts +2 -0
  150. package/dist/public-env.d.ts +37 -0
  151. package/dist/react-inspector.d.ts +54 -0
  152. package/dist/react-instrument.d.ts +18 -0
  153. package/dist/react-instrument.js +1 -0
  154. package/dist/react-props-panel-4XO3NSX3.js +1 -0
  155. package/dist/react-props-panel.d.ts +107 -0
  156. package/dist/react.d.ts +50 -0
  157. package/dist/react.js +1 -0
  158. package/dist/reaction-picker.d.ts +1 -0
  159. package/dist/review-layout.d.ts +62 -0
  160. package/dist/select-menu-QAYV7NKJ.js +1 -0
  161. package/dist/select-menu.d.ts +5 -0
  162. package/dist/server.d.ts +8 -0
  163. package/dist/server.js +3348 -0
  164. package/dist/setup-browser.d.ts +1 -0
  165. package/dist/setup.d.ts +6 -0
  166. package/dist/setup.js +1 -0
  167. package/dist/shell-mode-SUYLWDWW.js +1 -0
  168. package/dist/shell-mode.d.ts +12 -0
  169. package/dist/shell-session.d.ts +11 -0
  170. package/dist/shell.d.ts +14 -0
  171. package/dist/shell.js +1 -0
  172. package/dist/shortcut-help-XRDNX7MZ.js +1 -0
  173. package/dist/shortcut-help.d.ts +16 -0
  174. package/dist/shortcuts.d.ts +19 -0
  175. package/dist/snapshot-V264ZMQG.js +1 -0
  176. package/dist/snapshot.d.ts +49 -0
  177. package/dist/source-info.d.ts +10 -0
  178. package/dist/static-svg.d.ts +3 -0
  179. package/dist/styles.d.ts +1 -0
  180. package/dist/tag-editor-MMAMOEI4.js +1 -0
  181. package/dist/tag-editor.d.ts +14 -0
  182. package/dist/text-preview.d.ts +28 -0
  183. package/dist/thread-scope.d.ts +11 -0
  184. package/dist/thread-snapshot-HLTNHS4N.js +1 -0
  185. package/dist/thread-snapshot.d.ts +54 -0
  186. package/dist/token-adapter.d.ts +71 -0
  187. package/dist/token-custom-UCA6CBOV.js +1 -0
  188. package/dist/token-custom.d.ts +22 -0
  189. package/dist/token-page-PGPWPUKU.js +1 -0
  190. package/dist/token-page.d.ts +63 -0
  191. package/dist/token-picker-WMWUKLKJ.js +1 -0
  192. package/dist/token-picker.d.ts +44 -0
  193. package/dist/toolbar-items.d.ts +13 -0
  194. package/dist/toolbar-runtime-QCZONSWE.js +1 -0
  195. package/dist/toolbar-runtime.d.ts +5 -0
  196. package/dist/typed-custom.d.ts +28 -0
  197. package/dist/types.d.ts +365 -0
  198. package/package.json +184 -4
  199. package/server/account-page.ts +231 -0
  200. package/server/digest.ts +8 -0
  201. package/server/iap.ts +165 -0
  202. package/server/import-project.ts +204 -0
  203. package/server/index.ts +2043 -0
  204. package/server/migrations/0001_comments.sql +54 -0
  205. package/server/migrations/0002_identity.sql +2 -0
  206. package/server/migrations/0003_accent_color.sql +1 -0
  207. package/server/migrations/0004_workspaces.sql +46 -0
  208. package/server/migrations/0005_domains.sql +6 -0
  209. package/server/migrations/0006_member_budget.sql +16 -0
  210. package/server/migrations/0007_revisions.sql +17 -0
  211. package/server/migrations/0008_starter_comments.sql +1 -0
  212. package/server/migrations/0009_retention.sql +19 -0
  213. package/server/migrations/0010_reviewer_history.sql +1 -0
  214. package/server/migrations/0011_project_management.sql +52 -0
  215. package/server/migrations/0012_setup_sites.sql +3 -0
  216. package/server/migrations/0013_project_sites.sql +8 -0
  217. package/server/migrations/0014_project_site_removals.sql +3 -0
  218. package/server/migrations/0015_query_efficiency.sql +6 -0
  219. package/server/migrations/0016_thread_tags.sql +1 -0
  220. package/server/migrations/0017_project_webhooks.sql +4 -0
  221. package/server/migrations/0018_personal_access_tokens.sql +25 -0
  222. package/server/migrations/0019_snapshots.sql +30 -0
  223. package/server/migrations/0020_thread_scope.sql +1 -0
  224. package/server/migrations/0021_session_origin.sql +4 -0
  225. package/server/node-webhooks.ts +126 -0
  226. package/server/project-management.ts +398 -0
  227. package/server/project-sites.ts +117 -0
  228. package/server/proxy-auth.ts +130 -0
  229. package/server/public-env.ts +40 -0
  230. package/server/secrets.d.ts +13 -0
  231. package/server/setup-client.txt +3083 -0
  232. package/server/setup-page.ts +15 -0
  233. package/server/snapshots.ts +249 -0
  234. package/server/tokens.ts +324 -0
  235. package/server/tsconfig.json +12 -0
  236. package/server/validation.ts +235 -0
  237. package/server/webhooks.ts +206 -0
  238. package/server/workspaces.ts +176 -0
  239. package/src/emoji.ts +7 -0
  240. package/src/page.ts +4 -0
  241. package/src/types.ts +371 -0
  242. package/vite/index.d.mts +29 -0
  243. package/vite/index.mjs +97 -0
  244. package/vite/metadata.mjs +196 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lué Studio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NODE.md ADDED
@@ -0,0 +1,166 @@
1
+ # Run the Palette API in a Node container
2
+
3
+ The browser widget and CLI use the same API protocol on Cloudflare and Node. This option uses PostgreSQL and does not change an existing Worker/D1 deployment. It starts one API service for your preview sites; each project key maps to one repository and an allowlist of site origins.
4
+
5
+ ## Configure
6
+
7
+ 1. In your website repository, install Palette and generate the Node project:
8
+
9
+ ```sh
10
+ npm install @hazem3500/palette
11
+ npx palette init --node \
12
+ --endpoint https://comments.example.com \
13
+ --origin https://pr-42.preview.example.com \
14
+ --preview-origin 'https://pr-*.preview.example.com' \
15
+ --repo your-org/your-repo \
16
+ --branch-scope \
17
+ --google-client-id YOUR_GOOGLE_CLIENT_ID
18
+ ```
19
+
20
+ This writes `.komo/node.env` and `.komo/owner-key`, adds them to `.gitignore`, and configures the existing widget and agent CLI. The preview wildcard matches one hostname label under a domain you control. The endpoint is the **public API origin**, not a preview URL. For local development it may use HTTP localhost. Keep the generated owner key private.
21
+
22
+ 2. Provide a PostgreSQL database with a dedicated user allowed to create tables and triggers. Replace the `DATABASE_URL` and `GOOGLE_CLIENT_SECRET` placeholders in `.komo/node.env`. Add the printed `https://comments.example.com/auth/google/callback` URL to the Google OAuth client's authorized redirect URIs. In Cloud Run, supply the same variables through service configuration and a secret store. Do not commit the environment file. The Node runtime does not enable hosted provisioning (`KOMO_HOSTED`).
23
+
24
+ To enable signed webhooks, generate a 32-byte encryption key with `openssl rand -base64 32` and provide `KOMO_WEBHOOK_KEYS` through your secret manager as `{"current":"key-id","keys":{"key-id":"BASE64_KEY"}}`. Never store the key in source control or expose it in logs. Webhook endpoints must use HTTPS on port 443; the Node sender rejects private or mixed DNS answers and pins the vetted public address for the request.
25
+
26
+ 3. Build this repository's image with `docker build -f Dockerfile.node -t komo-node .`. Run it with the generated variables and HTTPS in front. For a local check, set `PUBLIC_URL=http://localhost:8080` in `.komo/node.env` and run `docker run --env-file /path/to/your-website/.komo/node.env -p 8080:8080 komo-node`. For Cloud Run, set the container port to 8080. Cloud SQL or another reachable PostgreSQL service can be used through `DATABASE_URL`; the database connection URL must be reachable from the container. Startup applies the bundled schema under a database lock. `/health` returns `{"ok":true}` after migrations succeed.
27
+
28
+ The image installs the PostgreSQL client (`pg`). To run `@hazem3500/palette/node` outside it, install `pg` next to Palette: it is an optional peer, so browser installs of Palette never include it.
29
+
30
+ 4. Open the owner-claim URL printed by `palette init --node` and sign in with Google. The setup page claims ownership once; the owner can then manage approved sites and private access. The generated widget config contains your project key. Use the same project key and branch in the bot CLI.
31
+
32
+ For a guest-only project, you can skip the generator and set `DATABASE_URL`, `PUBLIC_URL`, and `PROJECTS` directly. For example, `PROJECTS={"review":{"repo":"your-org/your-repo","origins":["https://pr-*.preview.example.com"],"allowGuests":true}}`. Anyone on an approved site can then comment; do not use guest-only mode for confidential reviews. `PROJECTS` must remain on one line in an env file. Google sign-in requires both `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`.
33
+
34
+ ### Approved origins
35
+
36
+ Each `origins` entry in `PROJECTS` is an exact origin or a pattern:
37
+
38
+ - `https://pr-*.preview.example.com`: `*` in the leftmost host label matches one label of letters, digits and hyphens. It never spans dots.
39
+ - `https://dev.example.com:*`: a trailing `:*` matches any port on that host, and also no port, which is how browsers send the default port 443. It never widens the host or the scheme, so `https://evil-dev.example.com:3000` and `https://dev.example.com.evil.test:3000` don't match. It combines with a host wildcard, as in `https://*-preview.example.com:*`.
40
+ - `http://localhost:*` and `http://127.0.0.1:*`: local development on any port.
41
+
42
+ The server refuses to start on an invalid entry and names it, for example `PROJECTS.review has an invalid origin "https://dev.example.com:4*"`.
43
+
44
+ ### Serve under a path
45
+
46
+ If a reverse proxy serves Palette under a path, include it in `PUBLIC_URL`, for example `PUBLIC_URL=https://comments.example.com/komo`. Every URL Palette generates then includes `/komo`: sign-in links and popups, OAuth redirect URIs, setup and invitation pages, and snapshot links. Requests are accepted whether or not the proxy strips the path. Register OAuth redirect URIs with the path, for example `https://comments.example.com/komo/auth/google/callback`.
47
+
48
+ ### Sign in through an identity proxy
49
+
50
+ Behind a proxy that authenticates users and signs each request, Palette can trust the proxy's identity instead of running its own OAuth client. This is off by default. One provider is supported:
51
+
52
+ - **`KOMO_PROXY_AUTH=gcp-iap`:** Google Cloud Identity-Aware Proxy (IAP). Set **`KOMO_PROXY_AUDIENCE`** to the backend audience, `/projects/PROJECT_NUMBER/global/backendServices/SERVICE_ID`. Palette verifies the `x-goog-iap-jwt-assertion` ES256 JWT against Google's IAP keys. It caches them for an hour, and fetches them again for an unknown key ID, at most every 30 seconds. A key fetch times out after 5 seconds. After a failed fetch it waits 30 seconds before trying again, and keeps verifying with the last keys for up to 24 hours. It checks the issuer, audience, expiry and issue time, allowing 60 seconds of clock skew. The subject must be `accounts.google.com:DIGITS`. The account ID is `google:DIGITS`, the same ID Google OAuth sign-in uses, so an existing account signs in as itself. The email is stored in lower case. `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` aren't needed.
53
+ - **`KOMO_IAP_AUDIENCE`**, the older name, still works: set alone, it means `gcp-iap` with that audience.
54
+ - **`KOMO_AUTH_PATH_PREFIX`** (optional, for example `/backoffice`, older name `KOMO_AUTH_PREFIX`): serves the sign-in handlers only under this path, so the proxy can protect just that path. Without the prefix, those handlers return 404. It needs `KOMO_PROXY_AUTH`; set without one, the server doesn't start.
55
+
56
+ Any other `KOMO_PROXY_AUTH` value stops the server at startup with the list of supported providers.
57
+
58
+ The sign-in handlers are:
59
+
60
+ - `PREFIX/auth/google/authorize`: the browser popup. It checks the one-use sign-in state from `/auth/google/start`, mints a session for the proxy's account without a Google redirect, and posts it to the site that opened it. The page sends `Cache-Control: no-store` and a nonce-based Content Security Policy with `frame-ancestors 'none'`.
61
+ - `POST PREFIX/auth/proxy/session?project=KEY` (older name `/auth/iap/session`): agents and CLIs exchange their proxy identity for a project session, returned as `{"token":…,"user":…}`. It's limited to 60 sessions an hour per account and project. It's only for clients that send no `Origin` header. A request whose `Origin` isn't the server's own is refused, and the response never carries CORS headers, so a web page can't turn a visitor's proxy sign-in into a token it can read.
62
+
63
+ While a proxy is on:
64
+
65
+ - Google is the only sign-in method, and `/config` reports `google: true`. GitHub sign-in returns 503, and the GitHub and Google OAuth handlers return 403 or 404.
66
+ - Reads need a session and guests are off, whatever `KOMO_REQUIRE_SESSION_FOR_READS` and `KOMO_ALLOW_GUESTS` say.
67
+ - Only sessions the proxy signed in are accepted, on every route. Each session records how it was signed in. Sessions from before the proxy was turned on are refused with 401 `Your session expired. Sign in again.`, whether from Google OAuth, GitHub or a guest, and reviewers sign in again through the proxy.
68
+ - Personal access tokens are refused with 403, and `/config` reports `tokens: false`. A token would keep working after the proxy stopped vouching for its owner. Agents use `/auth/proxy/session` instead.
69
+ - Missing, forged, expired, future-dated or wrong-audience assertions are rejected.
70
+ - The rest of the API stays outside the proxy: the widget calls it cross-origin with the bearer session from the popup.
71
+ - IAP carries only an email, so reviewers show their initial. Pass the widget's `avatar` option to supply photo URLs. Reviewers who signed in with Google OAuth before keep their stored photo.
72
+
73
+ Make sure clients can't reach the prefixed handlers without going through the proxy. The signed assertion check rejects forged headers either way.
74
+
75
+ Proxy sign-in completes without a consent screen: a reviewer already signed in to the proxy gets a session as soon as the popup opens. Any page on an approved origin can open that popup, so keep `origins` tight. A `:*` port wildcard approves every port on that host, including any local development server.
76
+
77
+ ### Reader, guest and session options
78
+
79
+ Each option is off by default and works on Node and on a self-hosted Worker:
80
+
81
+ - **`KOMO_REQUIRE_SESSION_FOR_READS=true`:** listing or fetching threads and comments needs a valid session. Other reads, such as snapshots, usage and exports, already need one. Anonymous reads return 401 `Sign in to see comments.`, and `/config` leaves out `repo` for callers without a session. Guest sessions still count, unless guests are off.
82
+ - **`KOMO_ALLOW_GUESTS=false`:** refuses guest sign-in on every project with `Guest comments are disabled.`, and refuses existing guest sessions with the same message. `/config` then reports `guests: false` and `snapshots.guests: false`, even if a project sets `allowGuestSnapshots`. While guests are allowed, a project's own `allowGuests: false` still applies.
83
+ - **`KOMO_SESSION_MAX_AGE_DAYS=N`** (1 to 36500): new sessions expire after N days, and sessions issued more than N days ago are refused, so lowering the cap applies to every existing session. Without it, sessions last until sign-out (100 years). Sessions record when they were issued from D1 migration `0021_session_origin` and PostgreSQL migration `007_session_origin`. Older sessions are dated by the 100-year lifetime, which is exact for them. A session that some other server issued with a shorter lifetime looks older than it is, so a cap refuses it. Personal access tokens keep the expiry chosen when they were created.
84
+
85
+ The flags take only `true` or `false`. An invalid value stops the Node server at startup, and makes every Worker request fail with the option named in the logs.
86
+
87
+ On a self-hosted Worker, apply the D1 migrations (`wrangler d1 migrations apply`) before turning on `KOMO_PROXY_AUTH` or `KOMO_SESSION_MAX_AGE_DAYS`. Without migration 0021, sessions are still issued as before, but those two options fail every authenticated request with a log naming the migration. The Node server applies its migrations at startup.
88
+
89
+ ### Deploy behind Google Cloud IAP
90
+
91
+ Build the image with `docker build -f Dockerfile.node -t komo-node .`. It runs `pnpm --filter @hazem3500/palette build`, which builds the server with `build:node`, and starts `node packages/komo/dist/node.mjs`. Outside Docker, run `pnpm install --frozen-lockfile && pnpm --filter @hazem3500/palette build`, then `node packages/komo/dist/node.mjs`.
92
+
93
+ Put only `PREFIX/auth/google/authorize` and `PREFIX/auth/proxy/session` behind IAP. Leave the rest of the API outside it.
94
+
95
+ Google Cloud's external Application Load Balancer appends two `X-Forwarded-For` entries, the client's address and then the load balancer's forwarding rule address. Behind it alone, set `KOMO_TRUSTED_PROXY_HOPS=2`. Add one for each proxy after it that appends the address it received from, such as nginx with `$proxy_add_x_forwarded_for`: a load balancer plus nginx is `3`. A proxy that passes the header through unchanged adds nothing.
96
+
97
+ | Variable | Purpose | Default | Example |
98
+ | --- | --- | --- | --- |
99
+ | `DATABASE_URL` | PostgreSQL connection | required | `postgresql://komo:PASSWORD@postgres:5432/komo` |
100
+ | `PUBLIC_URL` | Public URL, including any path the gateway strips | required | `https://comments.example.com/komo` |
101
+ | `PROJECTS` | Project keys, repositories and approved origins, on one line | required | `{"site":{"repo":"your-org/your-repo","origins":["https://*-web.preview.example.com","https://dev.example.com:*"],"allowGuests":false}}` |
102
+ | `PORT` | Listen port | `8080` | `8080` |
103
+ | `KOMO_PROXY_AUTH` | Identity proxy provider | off | `gcp-iap` |
104
+ | `KOMO_PROXY_AUDIENCE` | IAP audience (older name `KOMO_IAP_AUDIENCE`) | unset | `/projects/PROJECT_NUMBER/global/backendServices/SERVICE_ID` |
105
+ | `KOMO_AUTH_PATH_PREFIX` | Path IAP protects for sign-in (older name `KOMO_AUTH_PREFIX`) | none | `/backoffice` |
106
+ | `KOMO_SESSION_MAX_AGE_DAYS` | Session lifetime cap | unset (100 years) | `30` |
107
+ | `KOMO_TRUSTED_PROXY_HOPS` | `X-Forwarded-For` entries trusted proxies append (older name `KOMO_PROXY_HOPS`) | `0` | `2` behind the load balancer alone; `3` with one appending proxy, such as nginx, after it |
108
+ | `KOMO_REQUIRE_SESSION_FOR_READS` | Reads need a session | `false` | forced `true` by the proxy; leave unset |
109
+ | `KOMO_ALLOW_GUESTS` | Guest sign-in | `true` | forced `false` by the proxy; leave unset |
110
+ | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` | Google OAuth | unset | not needed with IAP |
111
+ | `KOMO_TOKEN_PEPPER` | Secret mixed into access token hashes | unset | a long random secret |
112
+ | `KOMO_WEBHOOK_KEYS` | Webhook secret encryption keys | unset | see Configure, step 2 |
113
+
114
+ ### Move a patched upstream server to this package
115
+
116
+ For a server that ran upstream komo with a local IAP patch:
117
+
118
+ 1. Back up and upgrade the database as in [Upgrade a database from upstream komo](#upgrade-a-database-from-upstream-komo). Migration `007_session_origin.sql` adds two nullable columns to `sessions`, recording how and when each session was signed in. Existing rows keep NULL.
119
+ 2. Keep `KOMO_IAP_AUDIENCE`, or rename it to `KOMO_PROXY_AUTH=gcp-iap` plus `KOMO_PROXY_AUDIENCE`. `KOMO_PROXY_HOPS` keeps its meaning.
120
+ 3. Replace a patched session lifetime with `KOMO_SESSION_MAX_AGE_DAYS`. Behind the proxy, sessions issued before the upgrade aren't accepted, because nothing records that the proxy signed them in. Each reviewer and agent signs in through IAP once more. The popup does this without a prompt, and agents call `/auth/proxy/session`. Personal access tokens stop working behind the proxy.
121
+ 4. Set `PUBLIC_URL` to the external URL, including the path the gateway strips, and `KOMO_AUTH_PATH_PREFIX` to the path IAP protects. `/auth/google/start` then returns `https://HOST/PATH/PREFIX/auth/google/authorize`, so a proxy rewrite of that response is no longer needed. Palette serves the prefixed handlers itself.
122
+ 5. Point health checks at `GET /health`. Palette doesn't answer `GET /` with 200.
123
+ 6. Startup is stricter. A server now stops with a named error on:
124
+ - a `KOMO_PROXY_HOPS` value that isn't a single digit from 0 to 8, such as `01` or ` 1`;
125
+ - `KOMO_AUTH_PREFIX` set without a proxy;
126
+ - a sign-in prefix with `.` or `..` segments;
127
+ - a `PROJECTS` origin that isn't a valid pattern.
128
+
129
+ ## Connect a preview and bot
130
+
131
+ Use the existing widget options; only its API endpoint changes:
132
+
133
+ ```js
134
+ initPalette({
135
+ endpoint: "https://comments.example.com",
136
+ project: "YOUR_GENERATED_PROJECT_KEY",
137
+ repo: "your-org/your-repo",
138
+ scope: "branch",
139
+ branch: "pr-42",
140
+ });
141
+ ```
142
+
143
+ Point the existing CLI at the same endpoint, project, repository, and branch. `palette login` uses Google when configured; `KOMO_TOKEN` accepts a personal access token (create one at `/account?project=KEY` on your server; see Bots and CI in the README) or a previously issued session token for a non-interactive bot. Store that token as a secret. Access tokens are limited by their scopes and projects; session tokens have the same permissions as their user. Optionally set `KOMO_TOKEN_PEPPER` to a long random secret to mix into stored token hashes; changing it revokes every access token. For example:
144
+
145
+ ```sh
146
+ palette comments list --endpoint https://comments.example.com --project YOUR_GENERATED_PROJECT_KEY --repo your-org/your-repo --branch pr-42 --origin https://pr-42.preview.example.com
147
+ palette comments reply THREAD_ID --body "Fixed and verified" --endpoint https://comments.example.com --project YOUR_GENERATED_PROJECT_KEY --repo your-org/your-repo --branch pr-42 --origin https://pr-42.preview.example.com
148
+ palette comments resolve THREAD_ID --endpoint https://comments.example.com --project YOUR_GENERATED_PROJECT_KEY --repo your-org/your-repo --branch pr-42 --origin https://pr-42.preview.example.com
149
+ ```
150
+
151
+ The `--origin` value must be one approved preview origin. Each PR needs the same branch value in widget and bot to isolate its feedback. On the same project, an owner can edit approved sites after Google sign-in. Wildcard origins match one hostname label only, so `pr-*.preview.example.com` does not allow arbitrary subdomains of `example.com`.
152
+
153
+ ## Upgrade a database from upstream komo
154
+
155
+ A database created by upstream komo's Node server is upgraded in place. That server records schema version 1 in `komo_schema`. On first boot, this server takes the migration lock and finds version 1. It applies `002_thread_tags.sql` through `007_session_origin.sql` in one transaction and records version 7. These migrations only add tables, columns that have defaults, and the nullable session columns `via` and `created_at`. Existing threads get no tags and page scope, webhooks stay off, and existing sessions keep NULL in the new columns. No rows are rewritten or deleted. Existing sessions keep working, so reviewers stay signed in, unless a sign-in proxy is turned on: then only sessions the proxy signs in are accepted. If a step fails, the transaction rolls back and the server exits with the schema unchanged. Later boots find version 7 and change nothing. `test/postgres-upstream-upgrade.test.ts` checks this against upstream commit `738da17`.
156
+
157
+ 1. Back up first, with a `pg_dump` from the same or a newer PostgreSQL major version: `pg_dump --format=custom --no-owner --file komo-before-upgrade.dump "$DATABASE_URL"`.
158
+ 2. Stop the old server. Then start this one with the same `DATABASE_URL`. `/health` returns `{"ok":true}` after the migrations finish.
159
+
160
+ To roll back, restore the backup. Redeploying the old image is not enough: the upstream server refuses version 7 with `Unsupported PostgreSQL schema version`. Stop the server and create an empty database. Run `pg_restore --no-owner --exit-on-error --dbname "$NEW_DATABASE_URL" komo-before-upgrade.dump`, then start the old image with that URL. The backup doesn't include feedback written after the upgrade. To keep a copy, run `palette project export --out comments.json` against this server before you restore.
161
+
162
+ ## Database boundary
163
+
164
+ The API's storage contract is `prepare().bind().first()/all()/run()` plus atomic `batch()`. `server/postgres.ts` implements it with a PostgreSQL pool, parameterized statements, and one transaction per batch. `server/postgres/001_initial.sql` mirrors the SQLite schema and its quota, revision, retention, and deletion triggers; later numbered files, such as `005_snapshots.sql` for element snapshots (`bytea`), mirror later D1 migrations. Add a numbered PostgreSQL migration whenever the D1 schema changes. Existing D1 migrations and the Worker entry point stay separate. The Node server runs the same request handler; transport and storage are the only new adapters.
165
+
166
+ The Node process runs retention cleanup after requests, as the self-hosted Worker does. It handles `SIGTERM` by closing its HTTP listener and database pool. Database connection failures prevent startup; request errors return the existing API error format. Rate limits use the direct socket IP by default, and never trust a client's `X-Forwarded-For`. Behind trusted reverse proxies, set `KOMO_TRUSTED_PROXY_HOPS` (older name `KOMO_PROXY_HOPS`, 0 to 8) to the number of `X-Forwarded-For` entries those proxies append before the app. Most proxies append one; Google Cloud's external Application Load Balancer appends two (see [Deploy behind Google Cloud IAP](#deploy-behind-google-cloud-iap)). The client address is then that many entries from the right; entries further left are ignored. If the header has fewer entries, or that entry isn't an IP address, the socket IP is used. Check your load balancer’s forwarding behavior for the correct value, and only set it when requests can't bypass those proxies. Client-supplied `CF-Connecting-IP` is ignored. The Cloudflare Worker uses Cloudflare's `CF-Connecting-IP`.
package/NOTICE.md ADDED
@@ -0,0 +1,45 @@
1
+ # Morphing menu source
2
+
3
+ `src/MorphingMenu.tsx` (compiled into the shared `dist` chunks) and `src/morphing-menu-styles.ts` use the copyable component and CSS published by Danny Williams at https://dannyjpwilliams.com/playground/morphing-menu/, retrieved 2026-09-15. The page explicitly instructs users to copy these files and customize the actions. Credit for that component remains with Danny Williams.
4
+
5
+ Local changes: CSS injection into Shadow DOM, composed-path outside-click detection, review-specific actions and appearance.
6
+
7
+ ## Icons
8
+
9
+ Review icons use the official `@untitledui/icons` package from https://github.com/untitleduico/icons. The dependency supplies the original SVG artwork. No icon artwork is copied into this package.
10
+
11
+ ## Interaction reference
12
+
13
+ Component highlighting, compact growing composers, pin previews, and target-aware feedback copying were informed by Mesurer (https://github.com/ibelick/mesurer), by Julien Thibeaut, reviewed at version 0.1.5. These interactions are implemented here against the shared comments API and use Untitled UI icons.
14
+
15
+ ## Floating drag and edge snapping
16
+
17
+ `src/floating-drag.ts` and `resizeEdgeBox` adapt the gesture geometry, velocity smoothing, edge thresholds, and edge resize from https://github.com/tjcages/panels (`src/hooks/use-drag-resize.ts`).
18
+
19
+ The edge sidebar (`sidebar: "edge"`) reuses that drag math and follows the panels floating-panel pattern: a panel that docks to a viewport edge, parks off-screen while closed, and peeks out at the edge while collapsed (`src/edge-sidebar.ts` and the `--edge-park-x` / `--edge-peek-x` offsets). The open animation is the drawer expand: the same compress-then-spring, with the comment rows staggering in. The drawer stays at the bottom of the sidebar as tabs. The account dialog sits inside that panel, inset from the sides and top, with no scrim. Background mode keeps the viewport account column and overlay.
20
+
21
+ MIT License
22
+
23
+ Copyright (c) 2026 tjcages
24
+
25
+ Permission is hereby granted, free of charge, to any person obtaining a copy
26
+ of this software and associated documentation files (the "Software"), to deal
27
+ in the Software without restriction, including without limitation the rights
28
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ copies of the Software, and to permit persons to whom the Software is
30
+ furnished to do so, subject to the following conditions:
31
+
32
+ The above copyright notice and this permission notice shall be included in all
33
+ copies or substantial portions of the Software.
34
+
35
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41
+ SOFTWARE.
42
+
43
+ ## Bundled dependencies
44
+
45
+ The production build includes selected Motion and picker modules. Their original licenses are collected in `dist/THIRD_PARTY_NOTICES.txt`. React and the official icon package remain external dependencies.