You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 768eadb
Browse filesBrowse the repository at this point in the historyBrowse files
feat(browser-bridge): let a development host learn the extension it is given
An unpacked build's identity is derived from the folder it was loaded from,
so it is different on every machine. Asking the person to name it asked for
something they cannot know, and the answer changed with every checkout.
The development host learns the identity from the extension instead, and only
from a proof: the peer still has to answer the surface's challenge with the
installation key, and the identity is recorded only once it has.
- let the upgrade gate admit an identity the host does not know, in a
development host. The proof is still the decision, and a peer that cannot
prove is refused and receives no credential
- record the proved identity per surface and remember it, atomically and
owner-only, under the data directory
- keep a packaged host strict: it admits the published identity it was built
for, and refuses everything else at the gate
- cap how many challenges one pairing will have outstanding, so a peer that
connects and then goes quiet cannot hold them
- say so on the extension's options page: there is nothing to set on the
host side any more
Copy file name to clipboardExpand all lines: packages/browser-extension/src/options.ts
+2-2Lines changed: 2 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -126,8 +126,8 @@ async function renderIdentity(): Promise<void> {
126
126
: "This is a local build. Its identity comes from the folder it was loaded from, so it differs on every machine.";
127
127
identityNote.textContent=
128
128
kind==="published"
129
-
? "The released WebUI package already admits this extension, so there is nothing to set on the host side. The setting below is only for a development WebUI."
130
-
: "A development WebUI admits only the extensions you name, so start it with the setting below.";
129
+
? "Nothing to set on the host side. A released WebUI package admits the published extension, and a development WebUI learns this extension the first time it proves the installation key."
130
+
: "Nothing to set on the host side. A development WebUI learns this extension the first time it proves the installation key.";
131
131
}
132
132
133
133
/** Put a value on the clipboard, or say that the clipboard was not there. */
Copy file name to clipboardExpand all lines: packages/webui/docs/browser-use/03-extension-admission.md
+30-1Lines changed: 30 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -65,7 +65,7 @@ and it is the current state of both composition roots:
65
65
66
66
| Entry point | Line |`dev`| Bridge allowlist |
67
67
| --- | --- | --- | --- |
68
-
|`scripts/run-webui-server.mjs`|67|`dev: true`| development — `MAVIS_BROWSER_EDGE_EXTENSION_IDS`, `MAVIS_BROWSER_CHROME_EXTENSION_IDS`|
68
+
|`scripts/run-webui-server.mjs`|74|`dev: true`| development — the identities it has learned, plus`MAVIS_BROWSER_EDGE_EXTENSION_IDS`, `MAVIS_BROWSER_CHROME_EXTENSION_IDS`|
69
69
|`release/webui-npm/bin/mcode-webui.mjs`| 93 |`dev: true`|**store** — the published identity the package was built for, plus `MAVIS_BROWSER_STORE_EDGE_EXTENSION_IDS`, `MAVIS_BROWSER_STORE_CHROME_EXTENSION_IDS`|
70
70
71
71
So on a packaged WebUI today, the generic path already accepts any upgrade that passes the loopback
@@ -96,6 +96,35 @@ requires no extension id, no environment variable and no restart with a hand-wri
96
96
store allowlist variables remain for a self-hosted store build, and they add to the published
97
97
identity rather than replacing it.
98
98
99
+
## The development host learns the identity it is given
100
+
101
+
The same reasoning applies to a development host, and for a stronger reason. An unpacked build's id
102
+
is derived from the directory it was loaded from, so it is **different on every machine and every
103
+
path**: there is no constant to bake in, and a name the person has to go and look up is a name they
104
+
will get wrong. So the development host does not ask for one either.
105
+
106
+
It learns the identity from the extension, and only from a proof:
107
+
108
+
- The upgrade gate admits an identity the host does not know. This widens **who may try**, not
109
+
**who may act**: the peer still has to answer the surface's challenge with the installation key,
110
+
and a peer that cannot is refused and receives no credential.
111
+
- When the peer proves the installation key — or a credential this host already issued — the host
112
+
records the identity under `<dataDir>/browser-bridge/trusted-extensions.json` and admits it from
113
+
then on. The file is written atomically and owner-only, and a file that cannot be read reads as
114
+
"nothing is trusted yet", which costs the extension one more proof and no secret.
115
+
- The identities are per surface, and the peer's own `hello` still says which browser it is on. The
116
+
name selects **which secret to check against**, never the identity to believe (ADR 0019, A5).
117
+
118
+
A **packaged** host does not learn anything: its published identity is exact, and a packaged host
119
+
that admitted unknown origins would stop being the published extension. The development
120
+
allowlist variables still work on a development host, and they add to what it has learned rather
121
+
than replacing it.
122
+
123
+
The cost of the wider upgrade gate is a peer that connects and then goes quiet: it would hold a
124
+
challenge unanswered. Each pairing therefore caps how many challenges it will have outstanding at
125
+
once (`MAX_OUTSTANDING_CHALLENGES`), and the hello deadline closes the sockets that never introduce
126
+
themselves.
127
+
99
128
**`implemented`: the bridge admission path never reads `dev`.** The pairing proof is verified
100
129
unconditionally, and the browser switch is checked unconditionally. `admission.ts:135` holds an
101
130
explicit no-op `if (context.dev)` branch whose only purpose is to make the absence of a bypass
| Development |`scripts/run-webui-server.mjs`|the ones it has learned, plus `MAVIS_BROWSER_EDGE_EXTENSION_IDS`, `MAVIS_BROWSER_CHROME_EXTENSION_IDS`|
127
+
|**Packaged**|`release/webui-npm/bin/mcode-webui.mjs`|the published identity it was built for, plus `MAVIS_BROWSER_STORE_EDGE_EXTENSION_IDS`, `MAVIS_BROWSER_STORE_CHROME_EXTENSION_IDS`|
123
128
124
129
The packaged entry passes `packaged: true` to the bridge; the development entry does not.
0 commit comments