@marver-design/marver 0.10.2 → 0.11.1

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 (30) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/README.md +16 -3
  3. package/dist/auth-B5yuwnOq.mjs +494 -0
  4. package/dist/{build-zUf_kwKp.mjs → build-DI31MFTB.mjs} +3 -2
  5. package/dist/cli.mjs +6 -6
  6. package/dist/{collab-s3k5byM1.mjs → collab-CWaG3Q4w.mjs} +43 -11
  7. package/dist/{comments-J06jqCVV.mjs → comments-CRqaO2MH.mjs} +26 -11
  8. package/dist/{comments-BZBKhKRO.mjs → comments-DZyobpxG.mjs} +8 -2
  9. package/dist/{manifest-DIsp3ldB.mjs → config-t9coJ-Pq.mjs} +3 -233
  10. package/dist/{daemon-BpKBJZee.mjs → daemon-DSmaS453.mjs} +23 -5
  11. package/dist/{dev-D2PG6igI.mjs → dev-BWIL2ewj.mjs} +117 -13
  12. package/dist/{init-DwV0_d9G.mjs → init-BSclDg4I.mjs} +2 -1
  13. package/dist/manifest-C2tzkNaC.mjs +233 -0
  14. package/dist/marver-id-B8-3WiHk.mjs +411 -0
  15. package/dist/marver-id-gate-BDzW6ahN.mjs +728 -0
  16. package/dist/{plugin-C1sDewZ3.mjs → plugin-DjBFxEQ7.mjs} +12 -84
  17. package/dist/{profile-BkiWglVE.mjs → profile-DcsJyppw.mjs} +9 -6
  18. package/dist/secure-cookie-_K1Hsx8H.mjs +26 -0
  19. package/dist/{serve-CZqPnj19.mjs → serve-3APqaNSB.mjs} +196 -43
  20. package/dist/sync-DELGomPk.mjs +397 -0
  21. package/dist/update-DuWDj5nR.mjs +77 -0
  22. package/dist/utm-CxC3QN5X.mjs +20 -0
  23. package/package.json +1 -1
  24. package/src/shared/utm.ts +2 -2
  25. package/templates/instructions/configure.md +4 -0
  26. package/templates/instructions/publish.md +99 -16
  27. package/dist/auth-KQ9Aj-nB.mjs +0 -245
  28. package/dist/events-BMtBvvgU.mjs +0 -101
  29. package/dist/sync-BJKKmy1n.mjs +0 -150
  30. package/dist/{shot-D7I0MCLu.mjs → shot-Cyv3GN79.mjs} +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,167 @@
2
2
 
3
3
  Notable changes to `@marver-design/marver`. Format follows [Keep a Changelog](https://keepachangelog.com); versions follow semver.
4
4
 
5
+ ## 0.11.1 - 2026-08-27
6
+
7
+ ### Fixed
8
+
9
+ - **The password gate's cookie now gets `Secure` from the pinned origin too.** 0.11.0 fixed
10
+ this for the collaboration session cookie and missed the gate's own, which is set in a
11
+ different file and is the one most likely to be sitting behind a plain `proxy_pass` - it is
12
+ the door on every password-mode canvas. Same rule, same reason: `MARVER_PUBLIC_ORIGIN`
13
+ decides when it is set, and `X-Forwarded-Proto` is the fallback only when there is nothing
14
+ better. Set `MARVER_PUBLIC_ORIGIN=https://your-canvas.example.com` and the thirty-day gate
15
+ cookie is https-only regardless of what your proxy tells the process.
16
+
17
+ Canvases behind a proxy that does send `X-Forwarded-Proto` - Railway, Fly, Vercel, Caddy,
18
+ and nginx configured with `proxy_set_header` - were never affected.
19
+
20
+ ## 0.11.0 - 2026-08-27
21
+
22
+ ### Added
23
+
24
+ - **Marver Sign In: a canvas can ask people who they are, instead of asking for a shared
25
+ password.** Set `MARVER_ID_ISSUER=https://id.marver.design` (plus `MARVER_PUBLIC_ORIGIN`
26
+ and `MARVER_DATA_DIR`) and the gate becomes an identity gate. People sign in once - with
27
+ Google, or a code emailed to them - and every canvas gated this way opens without another
28
+ password. Nobody types a canvas password, so there is none to leak, rotate, or forget, and
29
+ revoking one person revokes exactly them. This is the better default for a team, and the
30
+ one we run ourselves.
31
+
32
+ **Your guest list stays yours.** The identity service proves *who somebody is* and has no
33
+ say in *where they may go*: an address gets in if it already has an account on your canvas,
34
+ holds an unexpired invite, or is `MARVER_OWNER_EMAIL` on a canvas with no accounts yet.
35
+ That decision is made on your disk, and the identity service is never told the answer.
36
+ Somebody with a perfectly valid Marver account who is not on your list gets nothing.
37
+
38
+ **The sovereign path is unchanged and is not going anywhere.** `MARVER_PASSWORD` still
39
+ gates a canvas with accounts that live entirely in your `MARVER_DATA_DIR`, and a served
40
+ canvas with no `MARVER_ID_ISSUER` set makes no outbound request at all - nothing to depend
41
+ on, nothing to phone home to. (Stated precisely, because it is the kind of promise that
42
+ should be: `marver serve` is silent. `marver dev` still makes the one anonymous registry
43
+ request it always has, once a day, to notice a new version - `MARVER_NO_UPDATE_CHECK=1`
44
+ turns it off - and a workspace you have connected to a published canvas still syncs its
45
+ comments with that canvas.) The two are alternatives rather than layers: running both
46
+ would weaken your invite list to "an account OR whoever has the password", so the identity
47
+ gate replaces the password gate rather than sitting beside it.
48
+
49
+ How it works, for anyone who wants to check rather than trust: the canvas mints a
50
+ single-use nonce bound to the browser that started the sign-in, the tab visits the identity
51
+ service and comes back with a short-lived ES256 assertion in the URL fragment - the one
52
+ part of a link no server ever receives - and the canvas verifies it against published JWKS
53
+ before issuing its own ordinary session. The assertion's audience is the canvas's exact
54
+ origin, port included, so one minted for one canvas is inert at another. The verifying half
55
+ is dependency-free and lives in this repo (`src/server/marver-id.ts`), which is why
56
+ pointing a canvas at a different issuer is a configuration change rather than a fork.
57
+
58
+ `MARVER_PUBLIC_ORIGIN` is required and the canvas will not start without it, in
59
+ development too. The audience cannot be inferred safely: nginx's documented
60
+ `proxy_pass http://localhost:PORT` rewrites `Host` and adds no `X-Forwarded-*`, so a
61
+ request from the internet is indistinguishable from a local one - and a canvas that
62
+ guessed would hand its caller a `http://localhost` audience and a cookie with no Secure
63
+ flag.
64
+
65
+ Sign-in fails closed: if the identity service is unreachable, existing sessions keep
66
+ working and new ones are refused. Nothing falls back to open.
67
+
68
+ **Managing people from the repo needs `MARVER_CLI_TOKEN`.** `marver comments invite`,
69
+ `revoke` and `sync` authenticate the CLI with a password, and an identity account has none -
70
+ so set `MARVER_CLI_TOKEN` on the canvas to a *generated* secret of 32 characters or more
71
+ (`openssl rand -hex 24`) and pass the same value back:
72
+
73
+ ```
74
+ MARVER_CLI_TOKEN='<that same value>' marver comments connect https://canvas.example.com
75
+ ```
76
+
77
+ (`--token` works too, but a secret on the command line is visible to anything that can
78
+ list processes.)
79
+
80
+ It acts as whoever owns the canvas, so it does nothing until somebody has signed in and
81
+ claimed it. Generate it rather than choosing one: nothing rate-limits this credential and
82
+ nothing slows a guess down, so its entropy is the whole defence, and a length floor is the
83
+ only part of entropy a program can check. The canvas refuses to start on a value that is too
84
+ short, and on one containing characters an `Authorization` header cannot carry - hex rather
85
+ than base64, so that a canvas never boots holding a secret it would go on to reject.
86
+
87
+ `connect` trades that secret for an ordinary session and stores THAT in
88
+ `~/.marver/canvases/`, so neither the secret nor the session lands in your repo. **Rotate
89
+ `MARVER_CLI_TOKEN` to revoke it**: every session it minted stops working the moment the
90
+ variable changes, and sessions people hold in their browsers are untouched. That is the
91
+ lever rather than `comments revoke`, because the session acts as the owner and a canvas
92
+ refuses to remove its last owner.
93
+
94
+ It is an environment variable, and not a page that hands out tokens to whoever is signed in,
95
+ for a reason worth stating plainly: authored frames run same-origin in a canvas. That is
96
+ deliberate and documented, and it means frame JavaScript can read `mv_c` and ride the
97
+ viewer's session - so any browser-reachable way to mint a durable credential is a way for a
98
+ frame to mint one silently and carry it off, an owner's if an owner is the one looking. A
99
+ browser-approved device flow was built for exactly this job and pulled before release for
100
+ exactly that reason; requiring a real top-level navigation narrowed it and did not close it,
101
+ because `Sec-Fetch-Site: same-origin` proves where a request came from and never that a
102
+ person meant it. An environment variable is on the other side of that line: it is never
103
+ sent to a page, and reaching it already means reaching the deployment.
104
+
105
+ The cost is honest. It is a static secret that rotates by redeploying, and it acts as the
106
+ owner rather than as a person, so it is an operator's credential and belongs with your other
107
+ deployment secrets. Per-member CLI credentials still want the frame isolation this release
108
+ does not have.
109
+
110
+ ### Changed
111
+
112
+ - **The gate refuses to be framed** - every gate response now carries
113
+ `Content-Security-Policy: frame-ancestors 'none'` and `X-Frame-Options: DENY`. A
114
+ credential form inside somebody else's page, under a transparent button, is the classic
115
+ clickjack, and `SameSite=Lax` does not help because a framed page on the same site still
116
+ carries its cookies. Called out because it is a behaviour change rather than an addition:
117
+ if you were embedding a password-gated canvas in an iframe, that stops working. Embedding
118
+ a canvas people have already signed into is unaffected - it is the gate itself that
119
+ refuses.
120
+
121
+ ### Fixed
122
+
123
+ - **`Secure` on the collaboration session cookie no longer depends on a header your proxy may
124
+ not send.** A canvas decided whether it was on https by reading `X-Forwarded-Proto`, and
125
+ nginx's own documented `proxy_pass http://localhost:PORT` sets no `X-Forwarded-*` at all. A
126
+ canvas served over https behind that configuration saw no header, concluded "not secure",
127
+ and issued a thirty-day session cookie the browser would happily send over plain http. Where
128
+ `MARVER_PUBLIC_ORIGIN` is set it now decides - a deliberate statement by whoever deployed
129
+ the canvas, rather than a guess about a proxy that may not be speaking. The header remains
130
+ the fallback only where there is nothing better, which is development on loopback.
131
+
132
+ This release fixed the collaboration session cookie only. The password gate's own cookie
133
+ still guessed from the header - see 0.11.1.
134
+
135
+ - **The collaboration credential has moved out of your repository, because `marver dev` was
136
+ serving it.** It lived at `design/.local/collab.json`, and the dev server puts the repository
137
+ on the web so frames can import from it. Authored frames run same-origin, so any frame could
138
+ `fetch('/design/.local/collab.json')` and carry off a live session for your published canvas.
139
+
140
+ It now lives in `~/.marver/canvases/`, keyed by the project's path - one file per canvas per
141
+ machine, outside anything that is served. `connect` writes there, `dev` moves an older
142
+ credential out of the repo the first time it runs, and nothing needs doing by hand.
143
+
144
+ Guarding the old location was tried first and is the reason for the move: a deny rule matched
145
+ the path asked for rather than the file served, so a repository containing
146
+ `leak.json -> design/.local/collab.json` walked straight past it - and after that was fixed,
147
+ `public/` and Vite's derived `index.html` candidates each did the same. Enumerating a
148
+ bundler's resolution rules is not a thing anyone finishes. Those guards stayed as depth,
149
+ `design/.local` is still never served whatever route a request takes, and Vite's own default
150
+ deny list is no longer replaced by ours.
151
+
152
+ If you have ever run `marver dev` on a repository you did not write, treat that credential as
153
+ exposed: change `MARVER_CLI_TOKEN` on the canvas, which ends every session it minted, or on a
154
+ password-account canvas `marver comments revoke` and reconnect.
155
+
156
+ - **A gated canvas is no longer publicly cacheable in identity mode.** The `Cache-Control`
157
+ decision keyed off the password verifier alone, so an identity-gated canvas marked its
158
+ assets `public, immutable` - an invitation for a shared CDN to serve somebody's private
159
+ frames to anybody who asked. Both modes are now `private, no-store`.
160
+
161
+ - **A missing cosmetic file no longer falls through to the bundle.** Favicon and logo paths
162
+ are allowed past the gate on the promise that they are favicons and logos; when no such
163
+ file existed, the hash-routing fallback handed an unauthenticated caller `index.html`
164
+ instead. For those paths a miss is now a miss.
165
+
5
166
  ## 0.10.2 - 2026-08-24
6
167
 
7
168
  ### Fixed
package/README.md CHANGED
@@ -28,6 +28,7 @@ Frames appear on the canvas the moment the files land. That's the loop.
28
28
  - **Everything hot-reloads.** The agent writes, you watch it land - live.
29
29
  - **True viewports.** Each frame is a real iframe: drag its edge and your actual breakpoints fire.
30
30
  - **Your agent answers on the canvas.** Tag `@marver` in a comment and it picks up the job, edits the real source, and replies in the thread - no wiring, on by default. See [Live Jam](#live-jam).
31
+ - **Feedback without a signup wall.** Publish the canvas, invite people by email, and they sign in as themselves - one free Marver account, Google or an emailed code, and it opens every canvas you ever share with them. Or keep it entirely self-hosted behind a shared password. See [Collaboration](#collaboration).
31
32
  - **No AI inside.** The designer is the coding agent you already run and pay for. `init` generates the `design/AGENTS.md` contract that teaches it the whole workflow.
32
33
 
33
34
  ## The canvas
@@ -40,9 +41,21 @@ Frames appear on the canvas the moment the files land. That's the loop.
40
41
 
41
42
  ## Collaboration
42
43
 
43
- - **Comments.** Google-Docs-style feedback pinned to actual elements. Press `c`, click a div inside a frame, write - the thread lives on that element and survives edits via a layered anchor (source semantics → structure → fuzzy text). Viewers on a published canvas comment with real names and avatars (invite-link accounts, no email infrastructure). `marver dev` syncs the same threads into `design/comments/*.jsonl`, where your agent works the queue: `npx marver comments list --open --json` → fork a variant → `resolve --addressed-in`. Live via SSE; one deploy, no extra services.
44
+ - **Comments.** Google-Docs-style feedback pinned to actual elements. Press `c`, click a div inside a frame, write - the thread lives on that element and survives edits via a layered anchor (source semantics → structure → fuzzy text). Viewers on a published canvas comment with real names and avatars - either a Marver account they already have, or an invite-link account on that canvas alone. `marver dev` syncs the same threads into `design/comments/*.jsonl`, where your agent works the queue: `npx marver comments list --open --json` → fork a variant → `resolve --addressed-in`. Live via SSE; one deploy, no extra services.
44
45
  - **Laser mode.** Press `l`: every element in every frame gets depth-hued outlines plus a hover label - the fastest way to see structure. Click any element to copy its full address (frame file + CSS path) for the agent.
45
- - **Publishing.** `npx marver build` exports a static canvas (default-closed: `design/publish.json` names what ships); `npx marver serve` hosts it with an optional password gate. One deploy on Railway, Docker, or any static host - the [publishing guide](docs/publish.md) has the one-pagers.
46
+ - **Publishing.** `npx marver build` exports a static canvas (default-closed: `design/publish.json` names what ships, and whether each board is `read` or `comment`); `npx marver serve` hosts it. One deploy on Railway, Docker, or any static host - the [publishing guide](docs/publish.md) has the one-pagers.
47
+
48
+ - **Two ways to let people in.** They are alternatives, not layers - pick one per canvas.
49
+
50
+ **Marver Sign In** (new in 0.11). Set `MARVER_ID_ISSUER=https://id.marver.design` and reviewers sign in as themselves, with Google or a six-digit code emailed to them. One free Marver account opens *every* canvas gated this way, so the second board you share costs them nothing: no new signup, no new password, no link to keep. You invite an email address and they are in.
51
+
52
+ That is the whole difference, and it is the difference between "I'll look later" and a comment actually landing on the board. It also means their real name and face ride along, so a thread is from a person rather than from an address.
53
+
54
+ **A shared password** (`MARVER_PASSWORD`). Fully self-hosted, no account anywhere but your own volume, nothing about your reviewers leaves your infrastructure - and still fully supported, not a legacy path. The trade is that every canvas is an island: reviewers claim an invite link and pick a name and password *on that canvas*, and do it again for the next one. Fine for one board, a toll on the fifth.
55
+
56
+ Either way the canvas runs on your infrastructure and stores its own comments and members. With Marver Sign In the identity service only ever tells your canvas that a verified address matched an entry on your invite list - it never sees your frames, your files, or your comments. Rights stay yours: `design/publish.json` decides which boards are readable and which are commentable, and `marver comments invite`/`revoke` decides who is on the list.
57
+
58
+ *Coming next:* one home for your account - every canvas you have been invited to, every canvas you have shared, and the access each one carries, in a single list. Today the account page at `id.marver.design` shows the canvases you have approved and lets you revoke them.
46
59
 
47
60
  ## Live Jam
48
61
 
@@ -61,7 +74,7 @@ The same glow, driven from the terminal. When your agent takes a request, it cre
61
74
  | `npx marver init` | Scaffold `design/` in this repo (safe to re-run; refreshes managed files) |
62
75
  | `npx marver dev` / `canvas` | Start the local canvas - hot reload, comments, Live Jam armed (`--port`, default 5199) |
63
76
  | `npx marver build` | Static export → `design/.dist`; what ships comes from `design/publish.json` (default-closed) |
64
- | `npx marver serve` | Serve the export; `MARVER_PASSWORD` gates it, `MARVER_DATA_DIR` persists comments + accounts |
77
+ | `npx marver serve` | Serve the export; `MARVER_ID_ISSUER` or `MARVER_PASSWORD` gates it, `MARVER_DATA_DIR` persists comments + accounts |
65
78
  | `npx marver comments …` | The agent's queue: `connect <url>` · `sync` · `list` · `reply` · `resolve` · `invite <email>` · `revoke <email>` |
66
79
  | `npx marver work …` | Working glow from the terminal: `start <scene/frame …>` · `done … \| --all` · `list` |
67
80