a simple rust terminal ui (tui) for setting up alternative plc rotation keys driven by: secure enclave hardware (not synced) or software-based keys (synced to icloud)
···01CODESIGN_IDENTITY="Apple Development: Your Name (XXXXXXXXXX)"
2BUNDLE_ID="com.yourcompany.plc-touch"
3TEAM_ID="XXXXXXXXXX"
000000
···1+# Development signing (./build.sh or ./build.sh dev)
2CODESIGN_IDENTITY="Apple Development: Your Name (XXXXXXXXXX)"
3BUNDLE_ID="com.yourcompany.plc-touch"
4TEAM_ID="XXXXXXXXXX"
5+6+# Release signing (./build.sh release)
7+# Requires a "Developer ID Application" certificate from developer.apple.com
8+DEVELOPER_ID="Developer ID Application: Your Name (XXXXXXXXXX)"
9+APPLE_ID="your@email.com"
10+NOTARIZE_PASSWORD="xxxx-xxxx-xxxx-xxxx" # App-specific password from appleid.apple.com
+1
.gitignore
···1011# Apple signing (contains device UDIDs and developer certificates)
12embedded.provisionprofile
013entitlements.plist
1415# Claude Code local settings
···1011# Apple signing (contains device UDIDs and developer certificates)
12embedded.provisionprofile
13+release.provisionprofile
14entitlements.plist
1516# Claude Code local settings
+98-9
build.sh
···14# CODESIGN_IDENTITY="Apple Development: Your Name (XXXXXXXXXX)"
15# BUNDLE_ID="com.yourcompany.plc-touch"
16# TEAM_ID="XXXXXXXXXX"
17-IDENTITY="${CODESIGN_IDENTITY:?Set CODESIGN_IDENTITY in .env or as env var}"
000018BUNDLE_ID="${BUNDLE_ID:-com.example.plc-touch}"
19TEAM_ID="${TEAM_ID:-XXXXXXXXXX}"
20000000000000000000021KEYCHAIN_ACCESS_GROUP="${TEAM_ID}.${BUNDLE_ID}" cargo build --release
2223# Create .app bundle
···2627cp target/release/plc-touch "$APP/Contents/MacOS/plc-touch"
2829-# Copy provisioning profile if it exists
30-if [ -f embedded.provisionprofile ]; then
31 cp embedded.provisionprofile "$APP/Contents/embedded.provisionprofile"
0032fi
3334cat > "$APP/Contents/Info.plist" << EOF
···54</plist>
55EOF
5657-# Generate entitlements from env vars
58ENTITLEMENTS_FILE=$(mktemp)
59-cat > "$ENTITLEMENTS_FILE" << EOF
00000000000000000060<?xml version="1.0" encoding="UTF-8"?>
61<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
62<plist version="1.0">
···70</dict>
71</plist>
72EOF
07374-codesign --force --sign "$IDENTITY" --entitlements "$ENTITLEMENTS_FILE" "$APP"
75-rm -f "$ENTITLEMENTS_FILE"
00000000000000000000000000000007677-echo "✓ Built and signed $APP"
78-echo " Run with: $APP/Contents/MacOS/plc-touch"
00000000000000
···14# CODESIGN_IDENTITY="Apple Development: Your Name (XXXXXXXXXX)"
15# BUNDLE_ID="com.yourcompany.plc-touch"
16# TEAM_ID="XXXXXXXXXX"
17+#
18+# For release builds, also set:
19+# DEVELOPER_ID="Developer ID Application: Your Name (XXXXXXXXXX)"
20+# APPLE_ID="your@email.com"
21+# NOTARIZE_PASSWORD="app-specific-password"
22BUNDLE_ID="${BUNDLE_ID:-com.example.plc-touch}"
23TEAM_ID="${TEAM_ID:-XXXXXXXXXX}"
2425+MODE="${1:-dev}"
26+27+case "$MODE" in
28+ dev)
29+ IDENTITY="${CODESIGN_IDENTITY:?Set CODESIGN_IDENTITY in .env}"
30+ ;;
31+ release)
32+ IDENTITY="${DEVELOPER_ID:?Set DEVELOPER_ID in .env for release builds}"
33+ ;;
34+ *)
35+ echo "Usage: ./build.sh [dev|release]"
36+ echo " dev — Development build with provisioning profile (default)"
37+ echo " release — Developer ID build with notarization for distribution"
38+ exit 1
39+ ;;
40+esac
41+42+echo "Building plc-touch ($MODE)..."
43+44KEYCHAIN_ACCESS_GROUP="${TEAM_ID}.${BUNDLE_ID}" cargo build --release
4546# Create .app bundle
···4950cp target/release/plc-touch "$APP/Contents/MacOS/plc-touch"
5152+# Embed provisioning profile
53+if [ "$MODE" = "dev" ] && [ -f embedded.provisionprofile ]; then
54 cp embedded.provisionprofile "$APP/Contents/embedded.provisionprofile"
55+elif [ "$MODE" = "release" ] && [ -f release.provisionprofile ]; then
56+ cp release.provisionprofile "$APP/Contents/embedded.provisionprofile"
57fi
5859cat > "$APP/Contents/Info.plist" << EOF
···79</plist>
80EOF
8182+# Generate entitlements
83ENTITLEMENTS_FILE=$(mktemp)
84+if [ "$MODE" = "release" ]; then
85+ # Developer ID with provisioning profile: includes application-identifier + keychain-access-groups
86+ cat > "$ENTITLEMENTS_FILE" << EOF
87+<?xml version="1.0" encoding="UTF-8"?>
88+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
89+<plist version="1.0">
90+<dict>
91+ <key>com.apple.application-identifier</key>
92+ <string>${TEAM_ID}.${BUNDLE_ID}</string>
93+ <key>keychain-access-groups</key>
94+ <array>
95+ <string>${TEAM_ID}.*</string>
96+ </array>
97+</dict>
98+</plist>
99+EOF
100+else
101+ # Dev: needs application-identifier for provisioning profile
102+ cat > "$ENTITLEMENTS_FILE" << EOF
103<?xml version="1.0" encoding="UTF-8"?>
104<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
105<plist version="1.0">
···113</dict>
114</plist>
115EOF
116+fi
117118+if [ "$MODE" = "release" ]; then
119+ # Release: Developer ID signing with hardened runtime (required for notarization)
120+ codesign --force --sign "$IDENTITY" \
121+ --options runtime \
122+ --timestamp \
123+ --entitlements "$ENTITLEMENTS_FILE" \
124+ "$APP"
125+126+ rm -f "$ENTITLEMENTS_FILE"
127+128+ echo "✓ Signed with Developer ID"
129+130+ # Create zip for notarization
131+ ZIP="target/release/plc-touch.zip"
132+ rm -f "$ZIP"
133+ ditto -c -k --keepParent "$APP" "$ZIP"
134+135+ echo "Submitting for notarization..."
136+ APPLE_ID_ARG="${APPLE_ID:?Set APPLE_ID in .env for notarization}"
137+ PASS_ARG="${NOTARIZE_PASSWORD:?Set NOTARIZE_PASSWORD in .env (app-specific password)}"
138+139+ xcrun notarytool submit "$ZIP" \
140+ --apple-id "$APPLE_ID_ARG" \
141+ --team-id "$TEAM_ID" \
142+ --password "$PASS_ARG" \
143+ --wait
144+145+ # Staple the notarization ticket to the app
146+ xcrun stapler staple "$APP"
147+148+ # Re-create zip with stapled app
149+ rm -f "$ZIP"
150+ ditto -c -k --keepParent "$APP" "$ZIP"
151152+ echo ""
153+ echo "✓ Built, signed, notarized, and stapled"
154+ echo " Distribute: $ZIP"
155+ echo " Run with: $APP/Contents/MacOS/plc-touch"
156+else
157+ # Dev: Apple Development signing
158+ codesign --force --sign "$IDENTITY" \
159+ --entitlements "$ENTITLEMENTS_FILE" \
160+ "$APP"
161+162+ rm -f "$ENTITLEMENTS_FILE"
163+164+ echo ""
165+ echo "✓ Built and signed (dev)"
166+ echo " Run with: $APP/Contents/MacOS/plc-touch"
167+fi
+1-1
src/enclave.rs
···145 CFString::wrap_under_get_rule(kSecAttrSynchronizable),
146 CFBoolean::true_value().as_CFType(),
147 ));
148- // Use explicit access group so the key is findable across devices
149 attrs_pairs.push((
150 CFString::wrap_under_get_rule(kSecAttrAccessGroup),
151 CFString::new(keychain_access_group()).as_CFType(),
···145 CFString::wrap_under_get_rule(kSecAttrSynchronizable),
146 CFBoolean::true_value().as_CFType(),
147 ));
148+ // Explicit access group so the key is findable across devices
149 attrs_pairs.push((
150 CFString::wrap_under_get_rule(kSecAttrAccessGroup),
151 CFString::new(keychain_access_group()).as_CFType(),