Skip to content

Commit f22d989

Browse files
committed
Sign macOS CLI with stable identifier
1 parent 6c93338 commit f22d989

12 files changed

Lines changed: 154 additions & 21 deletions

File tree

.github/workflows/release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ jobs:
6666
if: contains(matrix.target, 'apple-darwin')
6767
run: cargo build --release --target ${{ matrix.target }}
6868

69+
- name: Ad-hoc sign macOS CLI binary
70+
if: contains(matrix.target, 'apple-darwin')
71+
run: bash scripts/codesign-macos-cli.sh target/${{ matrix.target }}/release/garyx
72+
6973
- name: Build (Linux — glibc 2.17 floor via cargo-zigbuild)
7074
if: contains(matrix.target, 'linux')
7175
run: cargo zigbuild --release --target ${{ matrix.target }}.2.17

AGENTS.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,16 @@ needs installed-app validation.
104104
- On macOS, do not treat a matching hash as sufficient after copying a locally
105105
built `garyx` binary into a launchd-managed path such as
106106
`/opt/homebrew/bin/garyx`. Clear removable target-file xattrs, ad-hoc re-sign
107-
the installed file, and verify it executes before restarting, otherwise
108-
launchd/AMFI may kill it with `OS_REASON_CODESIGNING`. `com.apple.provenance`
109-
can be inherited or protected on Homebrew paths even when `xattr -d` returns
110-
success, so do not rely on xattr output alone.
107+
the installed file with the stable identifier `com.bytedance.garyx` (or use
108+
`bash scripts/codesign-macos-cli.sh <path-to-garyx>`), and verify it executes
109+
before restarting, otherwise launchd/AMFI may kill it with
110+
`OS_REASON_CODESIGNING`. `com.apple.provenance` can be inherited or protected
111+
on Homebrew paths even when `xattr -d` returns success, so do not rely on
112+
xattr output alone.
113+
- For local macOS gateway development, prefer `scripts/install-local-cli.sh`
114+
after source changes. Release archives, `install.sh`, `garyx update`, and
115+
desktop `build:rust` should all preserve the same CLI identifier so directory
116+
authorization is not re-requested just because a new binary was installed.
111117
- Restart through the Garyx CLI with a wake target when working in an agent
112118
thread, for example `garyx gateway restart --wake thread <thread_id>
113119
--wake-message "continue"`.

CLAUDE.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,16 @@ needs installed-app validation.
104104
- On macOS, do not treat a matching hash as sufficient after copying a locally
105105
built `garyx` binary into a launchd-managed path such as
106106
`/opt/homebrew/bin/garyx`. Clear removable target-file xattrs, ad-hoc re-sign
107-
the installed file, and verify it executes before restarting, otherwise
108-
launchd/AMFI may kill it with `OS_REASON_CODESIGNING`. `com.apple.provenance`
109-
can be inherited or protected on Homebrew paths even when `xattr -d` returns
110-
success, so do not rely on xattr output alone.
107+
the installed file with the stable identifier `com.bytedance.garyx` (or use
108+
`bash scripts/codesign-macos-cli.sh <path-to-garyx>`), and verify it executes
109+
before restarting, otherwise launchd/AMFI may kill it with
110+
`OS_REASON_CODESIGNING`. `com.apple.provenance` can be inherited or protected
111+
on Homebrew paths even when `xattr -d` returns success, so do not rely on
112+
xattr output alone.
113+
- For local macOS gateway development, prefer `scripts/install-local-cli.sh`
114+
after source changes. Release archives, `install.sh`, `garyx update`, and
115+
desktop `build:rust` should all preserve the same CLI identifier so directory
116+
authorization is not re-requested just because a new binary was installed.
111117
- Restart through the Garyx CLI with a wake target when working in an agent
112118
thread, for example `garyx gateway restart --wake thread <thread_id>
113119
--wake-message "continue"`.

Cargo.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ members = [
1313
resolver = "2"
1414

1515
[workspace.package]
16-
version = "0.1.19"
16+
version = "0.1.20"
1717
edition = "2024"
1818
license = "MIT"
1919
repository = "https://github.com/Pyiner/garyx"

desktop/garyx-desktop/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"build": "npm run build:ui",
1414
"build:ui": "tsc --noEmit && electron-vite build",
1515
"build:web": "tsc --noEmit && vite build --config vite.web.config.ts",
16-
"build:rust": "cargo build --release -p garyx --manifest-path ../../Cargo.toml",
16+
"build:rust": "cargo build --release -p garyx --manifest-path ../../Cargo.toml && bash ../../scripts/codesign-macos-cli.sh ../../target/release/garyx",
1717
"build:packaged": "npm run build:ui",
1818
"preview": "electron-vite preview",
1919
"install:app": "node ./scripts/install-mac-app.mjs",

docs/installation.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ curl -fsSL https://raw.githubusercontent.com/Pyiner/garyx/main/install.sh | bash
2020
```bash [From source]
2121
git clone https://github.com/Pyiner/garyx
2222
cd garyx
23-
cargo build --release
24-
# binary: target/release/garyx
23+
scripts/install-local-cli.sh
2524
```
2625

2726
:::

garyx/src/commands.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ const CODEX_ENV_METADATA_KEY: &str = "desktop_codex_env";
9696
const CLAUDE_OAUTH_ENV: &str = "CLAUDE_CODE_OAUTH_TOKEN";
9797
const CODEX_API_KEY_ENV: &str = "OPENAI_API_KEY";
9898
const GITHUB_RELEASE_REPO: &str = "Pyiner/garyx";
99+
#[cfg(any(target_os = "macos", test))]
100+
const MACOS_CLI_CODESIGN_IDENTIFIER: &str = "com.bytedance.garyx";
99101
const DEFAULT_CHANNEL_AGENT_ID: &str = "claude";
100102

101103
#[derive(Debug, Deserialize)]
@@ -169,6 +171,45 @@ fn replacement_binary_path(
169171
Ok(std::env::current_exe()?)
170172
}
171173

174+
#[cfg(any(target_os = "macos", test))]
175+
fn macos_cli_codesign_args(binary_path: &Path) -> Vec<std::ffi::OsString> {
176+
let mut args = vec![
177+
std::ffi::OsString::from("--force"),
178+
std::ffi::OsString::from("--sign"),
179+
std::ffi::OsString::from("-"),
180+
std::ffi::OsString::from("--identifier"),
181+
std::ffi::OsString::from(MACOS_CLI_CODESIGN_IDENTIFIER),
182+
];
183+
args.push(binary_path.as_os_str().to_os_string());
184+
args
185+
}
186+
187+
#[cfg(target_os = "macos")]
188+
fn ad_hoc_codesign_macos_binary(binary_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
189+
let output = std::process::Command::new("/usr/bin/codesign")
190+
.args(macos_cli_codesign_args(binary_path))
191+
.output()?;
192+
if output.status.success() {
193+
return Ok(());
194+
}
195+
196+
let stdout = String::from_utf8_lossy(&output.stdout);
197+
let stderr = String::from_utf8_lossy(&output.stderr);
198+
Err(format!(
199+
"codesign failed for {} with identifier {}: {}{}",
200+
binary_path.display(),
201+
MACOS_CLI_CODESIGN_IDENTIFIER,
202+
stdout,
203+
stderr
204+
)
205+
.into())
206+
}
207+
208+
#[cfg(not(target_os = "macos"))]
209+
fn ad_hoc_codesign_macos_binary(_binary_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
210+
Ok(())
211+
}
212+
172213
fn register_plugin_state_logging(plugin_manager: &mut ChannelPluginManager) {
173214
plugin_manager.register_state_hook(|status| {
174215
tracing::info!(
@@ -1223,6 +1264,7 @@ pub(crate) async fn cmd_update(
12231264
perms.set_mode(0o755);
12241265
fs::set_permissions(&staged_path, perms)?;
12251266
}
1267+
ad_hoc_codesign_macos_binary(&staged_path)?;
12261268
fs::rename(&staged_path, &destination)?;
12271269

12281270
println!(

garyx/src/commands/tests.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,27 @@ fn detect_release_target_for_supported_platforms() {
11521152
assert!(detect_release_target_for("windows", "x86_64").is_err());
11531153
}
11541154

1155+
#[test]
1156+
fn macos_cli_codesign_args_use_stable_identifier() {
1157+
let args = macos_cli_codesign_args(Path::new("/tmp/garyx"));
1158+
let args = args
1159+
.iter()
1160+
.map(|arg| arg.to_string_lossy().into_owned())
1161+
.collect::<Vec<_>>();
1162+
1163+
assert_eq!(
1164+
args,
1165+
vec![
1166+
"--force",
1167+
"--sign",
1168+
"-",
1169+
"--identifier",
1170+
"com.bytedance.garyx",
1171+
"/tmp/garyx"
1172+
]
1173+
);
1174+
}
1175+
11551176
#[test]
11561177
fn parse_sha256_checksum_accepts_standard_release_file() {
11571178
let checksum = parse_sha256_checksum(

install.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ main() {
4949
mkdir -p "$INSTALL_DIR"
5050
cp "${tmpdir}/garyx-${version}-${target}/garyx" "$INSTALL_DIR/"
5151
chmod +x "$INSTALL_DIR/garyx"
52+
codesign_macos_cli "$INSTALL_DIR/garyx"
5253

5354
echo ""
5455
echo "Installed garyx to ${INSTALL_DIR}/garyx"
@@ -128,6 +129,19 @@ verify_checksum() {
128129
echo "Checksum OK."
129130
}
130131

132+
codesign_macos_cli() {
133+
local binary="$1"
134+
local identifier="com.bytedance.garyx"
135+
136+
if [ "$(uname -s)" != "Darwin" ]; then
137+
return 0
138+
fi
139+
140+
echo "Signing garyx with stable macOS identifier ${identifier}..."
141+
/usr/bin/codesign --force --sign - --identifier "$identifier" "$binary"
142+
/usr/bin/codesign --verify --verbose=2 "$binary"
143+
}
144+
131145
die() {
132146
echo "Error: $*" >&2
133147
exit 1

0 commit comments

Comments
 (0)