Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
97ab049
Add fail-closed macOS 27 selector driver
malpern Jul 25, 2026
9ec6b87
Provision desktop tools without Homebrew
malpern Jul 25, 2026
9830c36
Provision macOS 27 desktop automation base
malpern Jul 25, 2026
df51458
Use native Peekaboo lab host launcher
malpern Jul 25, 2026
a58dead
Harden secure Parallels credential delivery
malpern Jul 25, 2026
c78225a
Replace focused credential before secure delivery
malpern Jul 25, 2026
28b774e
Pace secure credential key pairs
malpern Jul 25, 2026
a9770d1
Route macOS 27 desktop leases to automation base
malpern Jul 25, 2026
3662660
Verify inherited macOS 27 console login
malpern Jul 25, 2026
0c91bf8
Use native RFB for protected macOS input
malpern Jul 25, 2026
d7ed9a9
Remove Peekaboo dependency from protected clicks
malpern Jul 25, 2026
2da2f8d
Handle System Settings protected input securely
malpern Jul 25, 2026
ef09ad7
Fix clean install VHID postcondition ordering
malpern Jul 25, 2026
d96bb46
Add verified repair and upgrade lab scenarios
malpern Jul 25, 2026
6f1a293
Include upgrade fixtures in lab archive identity
malpern Jul 25, 2026
b7fba03
Install upgrade fixtures through managed host control
malpern Jul 25, 2026
766e343
Attest managed Accessibility in runtime scenarios
malpern Jul 25, 2026
ce7441f
Recover Kanata after SMAppService bootout
malpern Jul 25, 2026
26d1c11
Harden reboot persistence lab proof
malpern Jul 25, 2026
64469af
Add uninstall and reinstall lifecycle proofs
malpern Jul 25, 2026
bd3e81e
Close running app before CLI uninstall
malpern Jul 25, 2026
971c7dc
Fix zsh scenario result recording
malpern Jul 25, 2026
bc3bd93
Support owned Tart guest reboots
malpern Jul 25, 2026
4b42b7a
Prove cancellation and recovery lifecycle
malpern Jul 25, 2026
5cad66b
Add safe nightly and weekly matrix planner
malpern Jul 25, 2026
2f09797
Use pinned guest tools in scenario runs
malpern Jul 25, 2026
3b13bea
Automate macOS 27 selector preparation
malpern Jul 25, 2026
9c4487b
Retry macOS 27 pane launch after quit
malpern Jul 25, 2026
2e7caa6
Package onsite physical remap proof
malpern Jul 25, 2026
d3e640e
Use detected mWave identity in onsite proof
malpern Jul 25, 2026
4996513
Add resumable scenario matrix executor
malpern Jul 26, 2026
29a938c
Include excluded matrix case titles
malpern Jul 26, 2026
ed07255
Ignore local lab campaign state
malpern Jul 26, 2026
4949ec3
Run matrix installs through product installer
malpern Jul 26, 2026
8eefee6
Allow artifact builds without local deployment
malpern Jul 26, 2026
b02f461
Reuse verified lab archives on resume
malpern Jul 26, 2026
592a756
Retain ignored lab payloads in archives
malpern Jul 26, 2026
2aa35fc
Harden managed clone enrollment recovery
malpern Jul 26, 2026
31654da
Read enveloped installer reports
malpern Jul 26, 2026
962ddae
Harden managed runtime recovery and attestation
malpern Jul 26, 2026
1431632
Harden clean install bootstrap and protected clicks
malpern Jul 26, 2026
c6360fe
Bind lab archives to the harness commit
malpern Jul 26, 2026
1570077
Accept harness-qualified archive keys
malpern Jul 26, 2026
4f56dae
Derive harness archives from verified product caches
malpern Jul 26, 2026
6726d6f
Handle sequential capture approval prompts
malpern Jul 26, 2026
8527917
Avoid resigning capture host during approval retries
malpern Jul 26, 2026
c2357c9
Scope notification occlusion to visible elements
malpern Jul 26, 2026
63b27e2
Require screen consent evidence before pausing
malpern Jul 26, 2026
f94c829
Qualify full-screen notification occlusion
malpern Jul 26, 2026
df8d65a
Ignore full-screen notification container groups
malpern Jul 26, 2026
ff03911
Retain completed installer approval checkpoints
malpern Jul 26, 2026
8e77648
Automate managed Input Monitoring approval
malpern Jul 26, 2026
cc20bb6
Recover installer approval after transport loss
malpern Jul 26, 2026
df23051
Open privacy settings through Standard Additions
malpern Jul 26, 2026
5856ad8
Prepare privacy UI without app scripting
malpern Jul 26, 2026
8a5d358
Wait for runtime convergence after reboot
malpern Jul 26, 2026
8f0f61b
Bound desktop consent inspection
malpern Jul 26, 2026
6592543
Invalidate cached runtime after uninstall
malpern Jul 26, 2026
18c315a
Route macOS 26 selectors to desktop base
malpern Jul 26, 2026
bdf0af3
Accept macOS 26 accessibility selector variants
malpern Jul 26, 2026
40f5833
Compose retained upgrade fixtures on lab host
malpern Jul 26, 2026
ae3bfe9
Run macOS 26 upgrade through owned controller
malpern Jul 26, 2026
111a733
Admit upgrade evidence steps in campaign runner
malpern Jul 26, 2026
e3444c0
Capture Kanata service failure evidence
malpern Jul 26, 2026
b764a4d
Preserve managed base for desktop leases
malpern Jul 26, 2026
375bde3
Model Parallels no-input upgrade boundary
malpern Jul 26, 2026
8e9beee
Record launch and helper health evidence
malpern Jul 26, 2026
c029ff5
Launch GUI before cancellation checkpoint
malpern Jul 26, 2026
158b120
Record successful lab scenario evidence
malpern Jul 26, 2026
8ca4bb1
Require only durable repair evidence
malpern Jul 26, 2026
33f6ed6
Harden secure dialog submission
malpern Jul 26, 2026
5a8f2d3
Add Pico 2 W physical HID fixture
malpern Jul 26, 2026
2851518
Add Waveshare ESP32-S3 HID fixture firmware
malpern Jul 26, 2026
ecf826f
Animate physical fixture campaign results
malpern Jul 27, 2026
a9da20d
Prepare ESP32 fixture for first-board install
malpern Jul 27, 2026
16e8650
Add Hacker Dojo boot splash
malpern Jul 27, 2026
f24be33
Document HID fixture readiness and UX
malpern Jul 27, 2026
aa2b3ca
Route CLI service control through privileged helper
malpern Jul 28, 2026
120063a
Add fail-closed physical HID capture matrix
malpern Jul 28, 2026
0f87d43
Merge remote-tracking branch 'origin/master' into codex/pico-hid-fixture
malpern Jul 28, 2026
acf07f2
Apply pinned Swift formatting
malpern Jul 28, 2026
25b67a5
Record resource-gated Shift matrix attempt
malpern Jul 28, 2026
0219682
Record passing Shift timing smoke matrix
malpern Jul 28, 2026
f5a9022
Document full physical Shift timing matrix
malpern Jul 28, 2026
592c9aa
Harden physical HID demos and evidence capture
malpern Jul 29, 2026
4ca0a77
Merge remote-tracking branch 'origin/master' into codex/pico-hid-fixture
malpern Jul 29, 2026
6c050bd
Record post-update fixture control soak
malpern Jul 29, 2026
aa22933
Add reliable one-command HID showroom proof
malpern Jul 29, 2026
3dc8e7f
Bypass idle admission for HID demos only
malpern Jul 29, 2026
3c8675d
Extend HID demos to twenty seconds
malpern Jul 29, 2026
f04606e
Restart Jig before HID demos
malpern Jul 29, 2026
144ebc7
Record twenty-second HID showroom proof
malpern Jul 29, 2026
ed2ed02
Add strict repeated-key HID research matrix
malpern Jul 29, 2026
57af01f
Merge remote-tracking branch 'origin/master' into codex/pico-hid-fixture
malpern Jul 29, 2026
7292a5e
Give HID Jig its own visual identity
malpern Jul 29, 2026
d3e9f89
Set the Jig app icon at launch
malpern Jul 29, 2026
5e812fc
Add Typover Bear test monitor mode
malpern Jul 31, 2026
dc7c824
Add Bear branding to HID test displays
malpern Jul 31, 2026
a2f194d
Use Bear-specific test status colors
malpern Jul 31, 2026
56a0eb1
Add prioritized fixture Wi-Fi profiles
malpern Aug 1, 2026
fc54e8b
Brand physical HID tests by target app
malpern Aug 1, 2026
9246c91
Harden physical HID capture under system load
malpern Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ run-tests-automated.sh
.tmp/
output/
test-results/
.keypath-lab/

# Generated coverage reports (produced in CI; never commit)
coverage/
Expand Down
7 changes: 7 additions & 0 deletions Scripts/build-and-sign.sh
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,11 @@ else
create_sparkle_archive
fi

# A matrix build needs the exact signed artifact but must not replace or restart
# the operator's installed app. Keep the normal release behavior as the default.
if [ "${SKIP_DEPLOY:-0}" = "1" ]; then
echo "Skipping local deployment and restart (SKIP_DEPLOY=1)"
else
# Stop running KeyPath and kanata BEFORE replacing the app bundle.
# Replacing binaries while the process is live causes macOS to detect
# code page mismatches and kill the process with:
Expand Down Expand Up @@ -598,6 +603,8 @@ fi
# Publish help content to website
# ─────────────────────────────────────────────────────────────────────

fi

GHPAGES_DIR="$SCRIPT_DIR/../.worktrees/gh-pages"
if [ -d "$GHPAGES_DIR" ] && [ "${SKIP_WEBSITE:-0}" != "1" ] && [ "${SKIP_NOTARIZE:-}" != "1" ]; then
echo ""
Expand Down
24 changes: 24 additions & 0 deletions Scripts/lab/assert-app-version
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/zsh
set -euo pipefail

expected=${1:-}
[[ -n "$expected" ]] || {
print -u2 "Usage: $0 EXPECTED_VERSION"
exit 2
}

app=/Applications/KeyPath.app
plist="$app/Contents/Info.plist"
[[ -d "$app" && -f "$plist" ]] || {
print -u2 "app version assertion: KeyPath is not installed"
exit 1
}

/usr/bin/codesign --verify --deep --strict "$app"
actual=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$plist")
[[ "$actual" == "$expected" ]] || {
print -u2 "app version assertion: expected $expected, found $actual"
exit 1
}

print "app_version\t$actual"
134 changes: 134 additions & 0 deletions Scripts/lab/assert-runtime-state
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""Independently assert the installed KeyPath runtime is ready or degraded."""

import json
import os
import subprocess
import sys


def run(command: list[str]) -> subprocess.CompletedProcess[str]:
return subprocess.run(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)


def main() -> int:
if len(sys.argv) not in {2, 4} or sys.argv[1] not in {"ready", "degraded"}:
print(
f"Usage: {sys.argv[0]} ready|degraded "
"[--managed-policy-manifest PATH]",
file=sys.stderr,
)
return 2
managed_manifest = None
if len(sys.argv) == 4:
if sys.argv[2] != "--managed-policy-manifest":
print("runtime assertion: invalid managed-policy option", file=sys.stderr)
return 2
managed_manifest = sys.argv[3]

expected = sys.argv[1]
cli = os.environ.get(
"KEYPATH_LAB_CLI",
"/Applications/KeyPath.app/Contents/MacOS/keypath-cli",
)
launchctl = os.environ.get("KEYPATH_LAB_LAUNCHCTL", "/bin/launchctl")
tcp_probe = os.environ.get(
"KEYPATH_LAB_TCP_PROBE",
os.path.join(os.path.dirname(__file__), "probe-kanata-tcp"),
)

status_result = run([cli, "service", "status", "--json"])
if status_result.returncode not in {0, 1}:
print(status_result.stderr, file=sys.stderr, end="")
return status_result.returncode
try:
payload = json.loads(status_result.stdout)
except json.JSONDecodeError as error:
print(f"runtime assertion: invalid service-status JSON: {error}", file=sys.stderr)
return 1
status = payload.get("data", payload)
if not isinstance(status, dict):
print("runtime assertion: service-status payload is not an object", file=sys.stderr)
return 1

launchd_result = run([launchctl, "print", "system/com.keypath.kanata"])
launchd_running = (
launchd_result.returncode == 0
and "state = running" in launchd_result.stdout
)

if expected == "degraded":
degraded = (
status.get("isOperational") is False
and status.get("kanataRunning") is False
and not launchd_running
)
if not degraded:
print(
"runtime assertion: expected a stopped Kanata service, "
f"got isOperational={status.get('isOperational')!r}, "
f"kanataRunning={status.get('kanataRunning')!r}, "
f"launchdRunning={launchd_running!r}",
file=sys.stderr,
)
return 1
print("runtime_state\tdegraded")
return 0

managed_accessibility_attested = False
if status.get("kanataAccessibility") is False and managed_manifest:
verify_lane = os.environ.get(
"KEYPATH_LAB_VERIFY_LANE",
os.path.join(os.path.dirname(__file__), "mdm", "verify-lane"),
)
managed_result = run(
[
verify_lane,
"managed-functional",
"--manifest",
managed_manifest,
]
)
if managed_result.returncode != 0:
print(managed_result.stderr, file=sys.stderr, end="")
return managed_result.returncode
managed_accessibility_attested = True

required = [
"isOperational",
"helperInstalled",
"helperWorking",
"keyPathAccessibility",
"keyPathInputMonitoring",
"kanataInputMonitoring",
"kanataBinaryInstalled",
"karabinerDriverInstalled",
"vhidDeviceHealthy",
"kanataRunning",
"karabinerDaemonRunning",
"vhidHealthy",
]
if not managed_accessibility_attested:
required.append("kanataAccessibility")
failed = [key for key in required if status.get(key) is not True]
if failed or not status.get("helperVersion") or not launchd_running:
print(
"runtime assertion: runtime is not ready; "
f"falseOrMissing={failed}, helperVersion={status.get('helperVersion')!r}, "
f"launchdRunning={launchd_running!r}",
file=sys.stderr,
)
return 1

tcp_result = run([tcp_probe])
if tcp_result.returncode != 0:
print(tcp_result.stderr, file=sys.stderr, end="")
return tcp_result.returncode
if managed_accessibility_attested:
print("kanata_accessibility_evidence\tmanaged-policy-profile")
print("runtime_state\tready")
return 0


if __name__ == "__main__":
raise SystemExit(main())
108 changes: 108 additions & 0 deletions Scripts/lab/assert-uninstalled-state
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""Independently assert KeyPath-owned state is removed after uninstall."""

import argparse
import hashlib
import os
from pathlib import Path
import subprocess
import sys


OWNED_SYSTEM_PATHS = (
"/Applications/KeyPath.app",
"/Library/PrivilegedHelperTools/com.keypath.helper",
"/Library/LaunchDaemons/com.keypath.kanata.plist",
"/Library/LaunchDaemons/com.keypath.karabiner-vhiddaemon.plist",
"/Library/LaunchDaemons/com.keypath.karabiner-vhidmanager.plist",
"/Library/LaunchDaemons/com.keypath.helper.plist",
"/Library/LaunchDaemons/com.keypath.logrotate.plist",
"/Library/KeyPath/bin/kanata",
"/usr/local/etc/kanata",
"/usr/local/bin/keypath-logrotate.sh",
"/etc/newsyslog.d/com.keypath.conf",
)
OWNED_LAUNCHD_LABELS = (
"system/com.keypath.kanata",
"system/com.keypath.helper",
"system/com.keypath.karabiner-vhiddaemon",
"system/com.keypath.karabiner-vhidmanager",
"system/com.keypath.logrotate",
)
SHARED_DRIVER_ID = "org.pqrs.Karabiner-DriverKit-VirtualHIDDevice"


def run(command: list[str]) -> subprocess.CompletedProcess[str]:
return subprocess.run(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)


def rooted(root: Path, path: str) -> Path:
return root / path.removeprefix("/")


def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(65536), b""):
digest.update(chunk)
return digest.hexdigest()


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--preserved-config", required=True, type=Path)
parser.add_argument("--expected-sha256", required=True)
args = parser.parse_args()

root = Path(os.environ.get("KEYPATH_LAB_ROOT", "/"))
home = Path(os.environ.get("KEYPATH_LAB_HOME", str(Path.home())))
launchctl = os.environ.get("KEYPATH_LAB_LAUNCHCTL", "/bin/launchctl")
pgrep = os.environ.get("KEYPATH_LAB_PGREP", "/usr/bin/pgrep")
systemextensionsctl = os.environ.get(
"KEYPATH_LAB_SYSTEMEXTENSIONSCTL", "/usr/bin/systemextensionsctl"
)

failures: list[str] = []
remaining_paths = [str(rooted(root, path)) for path in OWNED_SYSTEM_PATHS if rooted(root, path).exists()]
user_paths = (
home / "Library/Application Support/KeyPath",
home / "Library/Logs/KeyPath",
)
remaining_paths.extend(str(path) for path in user_paths if path.exists())
remaining_paths.extend(str(path) for path in (home / "Library/Preferences").glob("com.keypath*.plist"))
if remaining_paths:
failures.append("owned paths remain: " + ", ".join(sorted(remaining_paths)))

active_labels = [label for label in OWNED_LAUNCHD_LABELS if run([launchctl, "print", label]).returncode == 0]
if active_labels:
failures.append("owned launchd jobs remain: " + ", ".join(active_labels))

if run([pgrep, "-f", "/Applications/KeyPath.app"]).returncode == 0:
failures.append("a KeyPath application process is still running")

if not args.preserved_config.is_file():
failures.append(f"preserved configuration sentinel is missing: {args.preserved_config}")
elif sha256(args.preserved_config) != args.expected_sha256:
failures.append("preserved configuration sentinel changed during uninstall")

extensions = run([systemextensionsctl, "list"])
driver_lines = [line for line in extensions.stdout.splitlines() if SHARED_DRIVER_ID in line]
if extensions.returncode != 0:
failures.append("system extension inventory failed")
elif not any("activated enabled" in line for line in driver_lines):
failures.append("the shared VirtualHID driver was removed or is not activated and enabled")

if failures:
for failure in failures:
print(f"uninstall assertion: {failure}", file=sys.stderr)
return 1

print("keypath_owned_state\tremoved")
print("user_configuration\tpreserved")
print("shared_vhid_driver\tpreserved")
print("uninstall_state\tverified")
return 0


if __name__ == "__main__":
raise SystemExit(main())
Binary file added Scripts/lab/assets/peekaboo-lab-host-arm64
Binary file not shown.
44 changes: 44 additions & 0 deletions Scripts/lab/assets/peekaboo-lab-host.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void) {
const char *home = getenv("HOME");
char executable[PATH_MAX];
char support[PATH_MAX];
char socket_path[PATH_MAX];

if (home == NULL || home[0] != '/') {
return 64;
}
if (snprintf(executable, sizeof(executable), "%s/.local/bin/peekaboo", home) >=
(int)sizeof(executable) ||
snprintf(support, sizeof(support), "%s/Library/Application Support/Peekaboo", home) >=
(int)sizeof(support) ||
snprintf(socket_path, sizeof(socket_path), "%s/daemon.sock", support) >=
(int)sizeof(socket_path)) {
return 65;
}

if (mkdir(support, 0700) != 0 && errno != EEXIST) {
return 73;
}
unlink(socket_path);
execl(executable,
executable,
"daemon",
"run",
"--mode",
"manual",
"--bridge-socket",
socket_path,
"--idle-timeout-seconds",
"31536000",
"--input-strategy",
"actionFirst",
(char *)NULL);
return 127;
}
38 changes: 38 additions & 0 deletions Scripts/lab/capture-controller-state
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/zsh
set -euo pipefail

output="$PWD/.keypath-lab/scenario-output/controller-capture"
logs="$output/logs"
service_logs="$output/service-logs"
capture_home="${KEYPATH_CAPTURE_USER_HOME:-$HOME}"

mkdir -p "$logs" "$service_logs"
sw_vers > "$output/sw-vers.txt"
date -u +%Y-%m-%dT%H:%M:%SZ > "$output/captured-at.txt"
cp -R "$capture_home/Library/Logs/KeyPath/." "$logs/" 2>/dev/null || true

/bin/launchctl print system/com.keypath.kanata > "$output/kanata-launchd.txt" 2>&1 || true
/bin/ps -axo pid,ppid,user,state,lstart,command > "$output/processes.txt" 2>&1 || true
/usr/bin/log show --last 10m --style compact \
--predicate 'process == "kanata" OR process == "kanata-launcher" OR eventMessage CONTAINS[c] "com.keypath.kanata"' \
> "$output/kanata-unified.log" 2>&1 || true

for source in \
/var/log/com.keypath.kanata.stdout.log \
/var/log/com.keypath.kanata.stderr.log
do
if [[ -r "$source" ]]; then
cp "$source" "$service_logs/${source:t}"
elif /usr/bin/sudo -n /usr/bin/test -r "$source" 2>/dev/null; then
/usr/bin/sudo -n /bin/cp "$source" "$service_logs/${source:t}" 2>/dev/null || true
fi
done

if [[ -x /Applications/KeyPath.app/Contents/MacOS/keypath-cli ]]; then
/Applications/KeyPath.app/Contents/MacOS/keypath-cli system inspect --json \
> "$output/system-inspect.json" 2>/dev/null || true
fi

if (( EUID == 0 )) && [[ -n "${KEYPATH_CAPTURE_OWNER:-}" ]]; then
/usr/sbin/chown -R "$KEYPATH_CAPTURE_OWNER":staff "$output" 2>/dev/null || true
fi
Loading