2 Commits
Author SHA1 Message Date
andwati 3fa70b1df3 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.
2026-08-12 16:03:13 +03:00
andwati 8b5fd86cce Add privacy policy for Chrome Web Store listing 2026-08-12 15:50:14 +03:00
8 changed files with 87 additions and 72 deletions
+4 -25
View File
@@ -18,34 +18,14 @@ jobs:
- name: Package extension - name: Package extension
# Chrome requires manifest.json at the zip root, both for Web Store # Chrome requires manifest.json at the zip root, both for Web Store
# upload and for manually loading unpacked -- so this one archives # upload and for manually loading unpacked -- so this one archives
# only extension/'s contents, not the whole repo. Keeps the pinned # only extension/'s contents, not the whole repo. No "key" field to
# "key" field, so unpacked loads get the stable, predictable ID. # 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: | run: |
cd extension cd extension
zip -r "../kget-capture-extension-${GITHUB_REF_NAME}.zip" . 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 - name: Package source
# Separately, the native-host installer bundle (install.sh, # Separately, the native-host installer bundle (install.sh,
# native-host/, keys/) needs the whole repo. git archive only # native-host/, keys/) needs the whole repo. git archive only
@@ -59,5 +39,4 @@ jobs:
files: | files: |
kget-capture-${{ github.ref_name }}.zip kget-capture-${{ github.ref_name }}.zip
kget-capture-extension-${{ github.ref_name }}.zip kget-capture-extension-${{ github.ref_name }}.zip
kget-capture-store-${{ github.ref_name }}.zip
generate_release_notes: true generate_release_notes: true
+33
View File
@@ -0,0 +1,33 @@
# Privacy Policy
KGet Capture does not collect, store, transmit, or sell any user data.
## What the extension does
When you right-click a link and choose "Download with KGet", or when
auto-capture is on and Chrome starts a download, the extension sends that
download's URL and filename to a native messaging host, a small helper
program installed locally on your own computer (see
[Install](README.md#install)). That helper hands the URL to KGet, the KDE
download manager, over D-Bus, also entirely on your own machine.
The only other data the extension stores is your auto-capture on/off
preference, saved locally via the `chrome.storage.local` API. It never
leaves your browser.
## What never happens
- No data is sent to any server operated by the developer or anyone else.
- No analytics, tracking, or telemetry of any kind.
- No data is sold, shared, or transferred to third parties.
- No account, sign-in, or personal information is collected.
## Source code
KGet Capture is open source. You can review exactly what it does at
[github.com/andwati/kget-capture](https://github.com/andwati/kget-capture).
## Contact
Questions about this policy can be raised via
[GitHub Issues](https://github.com/andwati/kget-capture/issues).
+18 -12
View File
@@ -92,8 +92,10 @@ per step 2 above.)
Then in `chrome://extensions`: enable **Developer mode**, click **Load Then in `chrome://extensions`: enable **Developer mode**, click **Load
unpacked**, and select the `extension/` directory (from your clone, or the unpacked**, and select the `extension/` directory (from your clone, or the
extracted extension zip). `install.sh` prints the extension's fixed ID so extracted extension zip). Chrome assigns this unpacked copy its own ID,
you can confirm it loaded correctly. 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 ## Usage
@@ -115,19 +117,18 @@ Store listing); that step can't be scripted.
Prebuilt releases are published on the Prebuilt releases are published on the
[Releases page](https://github.com/andwati/kget-capture/releases); each [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-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 - `kget-capture-extension-vX.Y.Z.zip`: just the extension, manifest at the
zip root, `key` field intact, for manual "Load unpacked" installs. zip root, ready for either a manual "Load unpacked" install or a Chrome
- `kget-capture-store-vX.Y.Z.zip`: same extension, but with `key` stripped, Web Store upload.
for uploading to the Chrome Web Store dashboard. The Store rejects any
manifest containing `key` ("key field is not allowed in manifest") since The published extension's canonical ID
it assigns its own permanent ID on first publish, which won't match the (`mkiffclapimhbjjlgkndlillglopbcic`) is hardcoded into
pinned ID (`keys/extension-id.txt`) the other zips use. After the first `native-host/com.kget_capture.host.json`'s `allowed_origins`; see
successful Store publish, update `native-host/com.kget_capture.host.json`'s [keys/README.md](keys/README.md) for why, and what to do for local unpacked
`allowed_origins` to include the Store-assigned ID alongside the pinned testing (which gets a different, machine-specific ID).
one, so both installs can reach the native host.
## Known limitations ## Known limitations
@@ -176,6 +177,11 @@ Prebuilt releases are published on the
observed in a non-finished state at least once, so it silently skips observed in a non-finished state at least once, so it silently skips
small/fast downloads that complete before that first observation. small/fast downloads that complete before that first observation.
## Privacy
KGet Capture doesn't collect, transmit, or store any user data. See
[PRIVACY.md](PRIVACY.md) for details.
## License ## License
MIT, see [LICENSE](LICENSE). MIT, see [LICENSE](LICENSE).
-1
View File
@@ -3,7 +3,6 @@
"name": "KGet Capture", "name": "KGet Capture",
"version": "1.0.6", "version": "1.0.6",
"description": "Send browser downloads to KGet (KDE Download Manager) on Linux.", "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"], "permissions": ["downloads", "contextMenus", "storage", "nativeMessaging"],
"background": { "background": {
"service_worker": "background.js" "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" echo "WARNING: 'notify-send' not found -- captures will still work, but you won't get a notification when they finish"
fi fi
EXT_ID=$(cat "$SCRIPT_DIR/keys/extension-id.txt")
cat <<EOF cat <<EOF
Install complete. 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) 1. Open chrome://extensions (or the equivalent for your browser)
2. Enable "Developer mode" (top-right toggle) 2. Enable "Developer mode" (top-right toggle)
3. Click "Load unpacked" and select: $SCRIPT_DIR/extension 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. Make sure KGet is running (or set to autostart) for captures to work.
EOF 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 `extension-id.txt` holds the extension's one canonical ID,
it's identical across every install (needed so the native messaging host's `mkiffclapimhbjjlgkndlillglopbcic`, assigned by the Chrome Web Store on
`allowed_origins` can be hardcoded instead of asking each user to paste their first publish. It's hardcoded into
extension ID). `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 `extension/manifest.json` intentionally has no `"key"` field: the Chrome Web
somewhere safe if you want to be able to reproduce the same key deterministically Store dashboard rejects any manifest that includes one ("key field is not
later; losing it just means a future re-key would get a new extension ID, it's allowed in manifest"), since it assigns its own permanent ID on first
not a security incident. publish instead.
- `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`).
## 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 ## Implication for local "Load unpacked" development
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
```
Rotating the key changes the extension ID, which means `extension/manifest.json`'s Since there's no pinned key, Chrome derives an unpacked install's ID from a
`"key"` and `native-host/com.kget_capture.host.json`'s `allowed_origins` both hash of its local path, which won't match
need updating to 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", "description": "Native messaging host that hands off download URLs to KGet via D-Bus",
"path": "__HOST_SCRIPT_PATH__", "path": "__HOST_SCRIPT_PATH__",
"type": "stdio", "type": "stdio",
"allowed_origins": ["chrome-extension://lgkhlfhejbffjnpcdlpjclicihnbjijp/"] "allowed_origins": ["chrome-extension://mkiffclapimhbjjlgkndlillglopbcic/"]
} }