Files
kestrove-desktop/native-messaging/install.sh
T
samiandClaude Opus 5.5 923c466dea M3: working desktop app + browser pairing channel
- Builds and runs (tauri build --debug); placeholder icons; persistent VaultCore::open in the
  OS app-data dir; Secret Key remembered on the device; tray keeps the app serving the extension.
- Auto-lock: idle (system idle on Windows/macOS, in-app activity on Linux), screen lock, sleep.
- Local IPC for kestrove-nmh: 0700 dir + 0600 Unix socket with peer-uid checks; Windows named
  pipe (first instance, local only, user DACL, client SID check) behind cfg.
- Pairing channel: X25519 → HKDF → 6-word fingerprint confirmed in both UIs, per-connection
  session keys, AES-256-GCM with strict counters. Test vectors shared with kestrove-browser.
- Broker: query/fill/totp/save with origin re-checks; pairing and save prompts in the UI.
- kestrove-nmh relays ciphertext only; native-host installers for Linux/macOS/Windows.
- Tests: unit tests, tests/nmh_stdio.rs (real kestrove-nmh over stdio), examples/test-host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 20:13:54 +09:00

106 lines
3.9 KiB
Bash
Executable File

#!/bin/sh
# Registers the Kestrove native-messaging host with the browsers of the current user (Linux, macOS).
# Windows: install.ps1.
#
# install.sh [--nmh /path/to/kestrove-nmh] [--chrome-id ID] [--firefox-id ID]
# [--browsers chrome,chromium,edge,brave,firefox] [--user-data-dir DIR] [--uninstall]
#
# With neither --browsers nor --user-data-dir it installs for every browser whose profile directory exists.
# --user-data-dir adds a Chromium-style profile dir started with --user-data-dir (Chromium then
# looks for NativeMessagingHosts/ inside it), e.g. for tests.
# allowed_origins / allowed_extensions pin the host to our extension ids (docs/04 A8).
set -eu
HOST=app.kestrove.nmh
HERE=$(cd "$(dirname "$0")" && pwd)
NMH=""
# Development ids: the unpacked Chrome build is pinned by the manifest key in kestrove-browser/scripts/build.mjs.
CHROME_ID=kogapfibnnpeapfcjkkpgjddbhnjfein
FIREFOX_ID=[email protected]
BROWSERS=""
USER_DATA_DIR=""
UNINSTALL=0
die() { echo "install.sh: $*" >&2; exit 1; }
while [ $# -gt 0 ]; do
case "$1" in
--nmh) NMH=$2; shift 2 ;;
--chrome-id) CHROME_ID=$2; shift 2 ;;
--firefox-id) FIREFOX_ID=$2; shift 2 ;;
--browsers) BROWSERS=$(echo "$2" | tr ',' ' '); shift 2 ;;
--user-data-dir) USER_DATA_DIR=$2; shift 2 ;;
--uninstall) UNINSTALL=1; shift ;;
-h|--help) sed -n '2,13p' "$0"; exit 0 ;;
*) die "unknown option $1" ;;
esac
done
echo "$CHROME_ID" | grep -Eq '^[a-p]{32}$' || die "--chrome-id must be 32 letters a-p"
echo "$FIREFOX_ID" | grep -Eq '^[A-Za-z0-9._@{}-]+$' || die "bad --firefox-id"
if [ "$UNINSTALL" = 0 ]; then
if [ -z "$NMH" ]; then
# Next to this script (installed layout), else the cargo build output (development).
for c in "$HERE/kestrove-nmh" "$HERE/../src-tauri/target/release/kestrove-nmh" "$HERE/../src-tauri/target/debug/kestrove-nmh"; do
[ -x "$c" ] && NMH=$c && break
done
fi
[ -n "$NMH" ] || die "kestrove-nmh not found; pass --nmh"
[ -x "$NMH" ] || die "$NMH is not executable"
NMH=$(cd "$(dirname "$NMH")" && pwd)/$(basename "$NMH")
case "$NMH" in *[\"\\\|\&]*) die "the host path may not contain quotes, backslashes, | or &" ;; esac
fi
case "$(uname -s)" in
Darwin)
BASE="$HOME/Library/Application Support"
dir_chrome="$BASE/Google/Chrome"
dir_chromium="$BASE/Chromium"
dir_edge="$BASE/Microsoft Edge"
dir_brave="$BASE/BraveSoftware/Brave-Browser"
dir_firefox="$BASE/Mozilla"
ff_sub=NativeMessagingHosts
;;
Linux)
CFG=${XDG_CONFIG_HOME:-$HOME/.config}
dir_chrome="$CFG/google-chrome"
dir_chromium="$CFG/chromium"
dir_edge="$CFG/microsoft-edge"
dir_brave="$CFG/BraveSoftware/Brave-Browser"
dir_firefox="$HOME/.mozilla"
ff_sub=native-messaging-hosts
;;
*) die "unsupported OS $(uname -s); on Windows use install.ps1" ;;
esac
# Auto-detect only when nothing was named: a test run with --user-data-dir must not touch real profiles.
[ -n "$BROWSERS$USER_DATA_DIR" ] || for b in chrome chromium edge brave firefox; do
eval "d=\$dir_$b"
[ -d "$d" ] && BROWSERS="$BROWSERS $b"
done
render() { # template → stdout
sed -e "s|__NMH_PATH__|$NMH|" -e "s|__CHROME_EXTENSION_ID__|$CHROME_ID|" -e "s|__FIREFOX_EXTENSION_ID__|$FIREFOX_ID|" "$HERE/$HOST.$1.json"
}
place() { # flavor dir
if [ "$UNINSTALL" = 1 ]; then
rm -f "$2/$HOST.json" && echo "removed $2/$HOST.json"
else
mkdir -p "$2"
render "$1" > "$2/$HOST.json.tmp" && chmod 644 "$2/$HOST.json.tmp" && mv "$2/$HOST.json.tmp" "$2/$HOST.json"
echo "installed $2/$HOST.json"
fi
}
for b in $BROWSERS; do
case "$b" in
chrome|chromium|edge|brave) eval "d=\$dir_$b"; place chrome "$d/NativeMessagingHosts" ;;
firefox) place firefox "$dir_firefox/$ff_sub" ;;
*) die "unknown browser $b" ;;
esac
done
[ -z "$USER_DATA_DIR" ] || place chrome "$USER_DATA_DIR/NativeMessagingHosts"
[ -n "$BROWSERS$USER_DATA_DIR" ] || echo "no supported browser profile found (use --browsers)"