Make the Chrome Web Store ID the single canonical extension ID

The extension is now published (ID: mkiffclapimhbjjlgkndlillglopbcic).
Drop the pinned "key" field from manifest.json (the Store rejects it
anyway) and use the Store-assigned ID everywhere instead of juggling
two IDs: native-host/com.kget_capture.host.json's allowed_origins,
keys/extension-id.txt, and docs. This also removes the need for a
separate key-stripped release zip, since there's no key left to strip.
This commit is contained in:
2026-08-12 16:03:13 +03:00
parent 8b5fd86cce
commit 3fa70b1df3
7 changed files with 49 additions and 72 deletions
+4 -25
View File
@@ -18,34 +18,14 @@ jobs:
- name: Package extension
# Chrome requires manifest.json at the zip root, both for Web Store
# upload and for manually loading unpacked -- so this one archives
# only extension/'s contents, not the whole repo. Keeps the pinned
# "key" field, so unpacked loads get the stable, predictable ID.
# only extension/'s contents, not the whole repo. No "key" field to
# worry about: the Web Store's own assigned ID is now the single
# canonical one (see keys/README.md), so this same zip works for
# both a Store upload and a manual unpacked install.
run: |
cd extension
zip -r "../kget-capture-extension-${GITHUB_REF_NAME}.zip" .
- name: Package extension for Web Store upload
# The Chrome Web Store dashboard rejects any manifest containing a
# "key" field ("key field is not allowed in manifest") -- it assigns
# its own permanent ID on first publish instead. Strip it from a
# copy so this zip is upload-ready; the other extension zip above
# keeps "key" for stable unpacked/dev IDs.
run: |
rm -rf /tmp/store-extension
cp -r extension /tmp/store-extension
python3 -c "
import json
path = '/tmp/store-extension/manifest.json'
with open(path) as f:
manifest = json.load(f)
manifest.pop('key', None)
with open(path, 'w') as f:
json.dump(manifest, f, indent=2)
f.write('\n')
"
cd /tmp/store-extension
zip -r "$OLDPWD/kget-capture-store-${GITHUB_REF_NAME}.zip" .
- name: Package source
# Separately, the native-host installer bundle (install.sh,
# native-host/, keys/) needs the whole repo. git archive only
@@ -59,5 +39,4 @@ jobs:
files: |
kget-capture-${{ github.ref_name }}.zip
kget-capture-extension-${{ github.ref_name }}.zip
kget-capture-store-${{ github.ref_name }}.zip
generate_release_notes: true
+13 -12
View File
@@ -92,8 +92,10 @@ per step 2 above.)
Then in `chrome://extensions`: enable **Developer mode**, click **Load
unpacked**, and select the `extension/` directory (from your clone, or the
extracted extension zip). `install.sh` prints the extension's fixed ID so
you can confirm it loaded correctly.
extracted extension zip). Chrome assigns this unpacked copy its own ID,
different from the published extension's, so add it to `allowed_origins` in
`~/.config/<browser>/NativeMessagingHosts/com.kget_capture.host.json` (see
[keys/README.md](keys/README.md)) for the native host to accept it.
## Usage
@@ -115,19 +117,18 @@ Store listing); that step can't be scripted.
Prebuilt releases are published on the
[Releases page](https://github.com/andwati/kget-capture/releases); each
`vX.Y.Z` tag push builds and publishes one automatically, as three zips:
`vX.Y.Z` tag push builds and publishes one automatically, as two zips:
- `kget-capture-vX.Y.Z.zip`: full source, for `install.sh`/`install-remote.sh`.
- `kget-capture-extension-vX.Y.Z.zip`: just the extension, manifest at the
zip root, `key` field intact, for manual "Load unpacked" installs.
- `kget-capture-store-vX.Y.Z.zip`: same extension, but with `key` stripped,
for uploading to the Chrome Web Store dashboard. The Store rejects any
manifest containing `key` ("key field is not allowed in manifest") since
it assigns its own permanent ID on first publish, which won't match the
pinned ID (`keys/extension-id.txt`) the other zips use. After the first
successful Store publish, update `native-host/com.kget_capture.host.json`'s
`allowed_origins` to include the Store-assigned ID alongside the pinned
one, so both installs can reach the native host.
zip root, ready for either a manual "Load unpacked" install or a Chrome
Web Store upload.
The published extension's canonical ID
(`mkiffclapimhbjjlgkndlillglopbcic`) is hardcoded into
`native-host/com.kget_capture.host.json`'s `allowed_origins`; see
[keys/README.md](keys/README.md) for why, and what to do for local unpacked
testing (which gets a different, machine-specific ID).
## Known limitations
-1
View File
@@ -3,7 +3,6 @@
"name": "KGet Capture",
"version": "1.0.6",
"description": "Send browser downloads to KGet (KDE Download Manager) on Linux.",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtM1nFg5pPd3UBzynUJHj6aUy7bjudZaX9AQTWGMqAX3fXylw1lubSzP1/BaS9ivvgOJg/loyEgzEZlcNPzblFVlg8BqEJ3tntdk2OzYvUs0pUdcPzzyjso9TykCkUUTI7ev8JkreW/FYlinfvNwSYN4/c1s3h8/I6XHtxOdmO1G3bS98+BQaXPldbDmxoKwyWR8u1f8gTO2Mg3GY4BHau3RWf1kgtNzh20p9wE3sEFdWkFu7ArgRCIrWLh6m3O6lolnHQSCWPwtOTx893Qkp9Eu6FwUnoWc8rsYx8sqa0eNCTCS6Kjr1Iu3yEJ5y9390jUiDZTQ/c62ueCTQaYv2bwIDAQAB",
"permissions": ["downloads", "contextMenus", "storage", "nativeMessaging"],
"background": {
"service_worker": "background.js"
+7 -5
View File
@@ -129,19 +129,21 @@ if ! command -v notify-send >/dev/null 2>&1; then
echo "WARNING: 'notify-send' not found -- captures will still work, but you won't get a notification when they finish"
fi
EXT_ID=$(cat "$SCRIPT_DIR/keys/extension-id.txt")
cat <<EOF
Install complete.
Extension ID: $EXT_ID
If you installed the extension from the Chrome Web Store, you're done --
reopen its toolbar popup and click "I ran it, check again" to confirm.
Next steps:
If you're loading it unpacked for development instead:
1. Open chrome://extensions (or the equivalent for your browser)
2. Enable "Developer mode" (top-right toggle)
3. Click "Load unpacked" and select: $SCRIPT_DIR/extension
4. Confirm the loaded extension's ID matches: $EXT_ID
4. Chrome will assign this unpacked copy its own ID, different from the
published extension's. Find it on chrome://extensions and add it to
"allowed_origins" in ~/.config/<browser>/NativeMessagingHosts/com.kget_capture.host.json
so the native host will accept connections from it too.
Make sure KGet is running (or set to autostart) for captures to work.
EOF
+23 -27
View File
@@ -1,32 +1,28 @@
# Extension signing key
# Extension ID
This directory holds the RSA key pair used to pin the extension's Chrome ID so
it's identical across every install (needed so the native messaging host's
`allowed_origins` can be hardcoded instead of asking each user to paste their
extension ID).
`extension-id.txt` holds the extension's one canonical ID,
`mkiffclapimhbjjlgkndlillglopbcic`, assigned by the Chrome Web Store on
first publish. It's hardcoded into
`native-host/com.kget_capture.host.json`'s `allowed_origins`, which is what
lets the native host trust connections from the extension without asking
each user to paste their own ID.
- `kget-capture-private.pem` — **not committed** (see `.gitignore`). Keep it
somewhere safe if you want to be able to reproduce the same key deterministically
later; losing it just means a future re-key would get a new extension ID, it's
not a security incident.
- `kget-capture-public.der` / `kget-capture-public.b64` — derived from the
private key. The base64 contents of `kget-capture-public.b64` is pasted
verbatim into `extension/manifest.json`'s `"key"` field. These are already
effectively public once checked into `manifest.json`, so committing them is
fine (and `extension-id.txt` too).
- `extension-id.txt` — the 32-character Chrome extension ID derived from the
public key (also hardcoded into `native-host/com.kget_capture.host.json`).
`extension/manifest.json` intentionally has no `"key"` field: the Chrome Web
Store dashboard rejects any manifest that includes one ("key field is not
allowed in manifest"), since it assigns its own permanent ID on first
publish instead.
## Regenerating (only if rotating the key)
`kget-capture-private.pem` / `kget-capture-public.der` / `kget-capture-public.b64`
are leftover from an earlier approach that pinned a separate ID for unpacked
installs via a local key pair. They're no longer referenced anywhere (the
`.pem` was never committed; see `.gitignore`) and can be ignored or removed.
```bash
openssl genrsa -out keys/kget-capture-private.pem 2048
openssl rsa -in keys/kget-capture-private.pem -pubout -outform DER -out keys/kget-capture-public.der
openssl base64 -A -in keys/kget-capture-public.der -out keys/kget-capture-public.b64
openssl dgst -sha256 -binary keys/kget-capture-public.der | head -c 16 | od -An -tx1 \
| tr -d ' \n' | tr '0123456789abcdef' 'abcdefghijklmnop' > keys/extension-id.txt
```
## Implication for local "Load unpacked" development
Rotating the key changes the extension ID, which means `extension/manifest.json`'s
`"key"` and `native-host/com.kget_capture.host.json`'s `allowed_origins` both
need updating to match.
Since there's no pinned key, Chrome derives an unpacked install's ID from a
hash of its local path, which won't match
`mkiffclapimhbjjlgkndlillglopbcic` and differs per machine/checkout. To test
an unpacked build against the native host locally, add your own unpacked
ID to `allowed_origins` in `~/.config/<browser>/NativeMessagingHosts/com.kget_capture.host.json`
after running `install.sh` (Chrome shows the unpacked ID on
`chrome://extensions` once loaded).
+1 -1
View File
@@ -1 +1 @@
lgkhlfhejbffjnpcdlpjclicihnbjijp
mkiffclapimhbjjlgkndlillglopbcic
+1 -1
View File
@@ -3,5 +3,5 @@
"description": "Native messaging host that hands off download URLs to KGet via D-Bus",
"path": "__HOST_SCRIPT_PATH__",
"type": "stdio",
"allowed_origins": ["chrome-extension://lgkhlfhejbffjnpcdlpjclicihnbjijp/"]
"allowed_origins": ["chrome-extension://mkiffclapimhbjjlgkndlillglopbcic/"]
}