Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 .github/workflows/mac-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
run: |
bash -n machogs
python3 -m unittest discover -s tests
node --test tests/test_website_analytics.mjs
plutil -lint app/PrivacyInfo.xcprivacy
- name: Test Mac core
run: swift test --package-path app
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Finds the hog. Names the app. Leaves live sessions alone.**

**[Download the Mac app](https://github.com/bnishit/machogs/releases/download/v1.4.2/Machogs-1.4.2.dmg)** · **[Website](https://bnishit.github.io/machogs/)** · free · macOS 13+ · Apple silicon + Intel
**[Download the Mac app](https://github.com/bnishit/machogs/releases/download/v1.4.3/Machogs-1.4.3.dmg)** · **[Website](https://bnishit.github.io/machogs/)** · free · macOS 13+ · Apple silicon + Intel

## The Mac app

Expand All @@ -14,7 +14,7 @@ is opt-in and limited to disconnected abandoned helpers.

**New in 1.4:** optional usage sharing, off by default. Say “No thanks” and use
every feature, or change your choice later in **Settings → Privacy**.
[Release notes](https://github.com/bnishit/machogs/releases/tag/v1.4.2) ·
[Release notes](https://github.com/bnishit/machogs/releases/tag/v1.4.3) ·
[Privacy details](https://bnishit.github.io/machogs/privacy.html)

The app needs macOS 13 or later. No admin password, Full Disk Access, or
Expand Down Expand Up @@ -189,7 +189,7 @@ belonging to a live Claude Code session — it tells you to quit it there.

## Install the Mac app

[Download Machogs 1.4.2](https://github.com/bnishit/machogs/releases/download/v1.4.2/Machogs-1.4.2.dmg), open the DMG, and drag Machogs to Applications.
[Download Machogs 1.4.3](https://github.com/bnishit/machogs/releases/download/v1.4.3/Machogs-1.4.3.dmg), open the DMG, and drag Machogs to Applications.

The app is signed with Developer ID, notarized by Apple, and includes both
Apple silicon and Intel code. The first scan only looks. To uninstall: turn off
Expand All @@ -205,7 +205,7 @@ command arguments, scan contents, messages or cleanup receipts.
|---|---|---|
| App: Settings → Privacy → Share feature usage | App opens, onboarding, bounded feature scan counts and manual action outcomes, version and random install ID | Self-hosted Native Analytics |
| CLI: explicitly set `MACHOGS_ANALYTICS=1` | Command category and success/failure, random install ID | Self-hosted Native Analytics |
| Website: allow on the privacy page | Landing-page visits and download clicks, unrelated ID per page load | PostHog |
| Website: allow on the privacy page | Landing-page visits and download clicks, unrelated ID per page load | Self-hosted Native Analytics |

The native receiver stores the connection IP; native events expire after 90 days.
App sharing requires a fresh choice when upgrading from the older basic usage
Expand Down
4 changes: 2 additions & 2 deletions app/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.4.2</string>
<string>1.4.3</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand All @@ -28,7 +28,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>14</string>
<string>15</string>
<key>LSMinimumSystemVersion</key>
<string>13.0</string>
<key>LSUIElement</key>
Expand Down
5 changes: 3 additions & 2 deletions app/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ esac

BINARIES=()
for arch in $ARCHS; do
swift build -c release --arch "$arch"
bin_dir=$(swift build -c release --arch "$arch" --show-bin-path)
scratch="$ROOT/.build/release-$arch"
swift build -c release --arch "$arch" --scratch-path "$scratch"
bin_dir=$(swift build -c release --arch "$arch" --scratch-path "$scratch" --show-bin-path)
BINARIES+=("$bin_dir/MachogsApp")
done

Expand Down
8 changes: 4 additions & 4 deletions docs/analytics-config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Public capture token only. Never put a personal/admin API key here.
// Public fixed-schema relay only. Never embed a producer or owner key here.
window.machogsAnalyticsConfig = Object.freeze({
token: 'phc_NKeUtSphlVzbxyDgYR58znBU0M0gAv6yjldCx65AOg4',
host: 'https://us.i.posthog.com',
version: '1.4.2'
host: 'https://native-analytics-production.up.railway.app',
project: 'machogs',
version: '1.4.3'
});
21 changes: 9 additions & 12 deletions docs/analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
const config = window.machogsAnalyticsConfig;
const test = new URLSearchParams(location.search).get('analytics_test') === '1';
const production = location.hostname === 'bnishit.github.io' && location.pathname.startsWith('/machogs/');
const available = config?.token && ['https://us.i.posthog.com', 'https://eu.i.posthog.com'].includes(config.host) && (production || test);
const key = test ? 'machogs.analytics.test.choice' : 'machogs.analytics.choice';
const available = config?.project === 'machogs' && config.host === 'https://native-analytics-production.up.railway.app' && (production || test);
const key = test ? 'machogs.native.analytics.test.choice' : 'machogs.native.analytics.choice';
const blocked = navigator.globalPrivacyControl === true || navigator.doNotTrack === '1';
let choice;
try { choice = localStorage.getItem(key); } catch { /* Choice lasts for this page only. */ }
Expand All @@ -17,18 +17,15 @@
if (!available || blocked || choice !== 'yes' || (!test && navigator.webdriver)) return;
if (placement !== undefined && !['nav', 'hero', 'install'].includes(placement)) return;
if (!crypto.randomUUID) return;
pageID ||= 'web_' + crypto.randomUUID();
pageID ||= crypto.randomUUID();
const controller = new AbortController();
pending.add(controller);
const payload = {
api_key: config.token, event, distinct_id: pageID, uuid: crypto.randomUUID(),
properties: {
product: 'machogs', platform: 'web', environment: test ? 'test' : 'production',
app_version: config.version, ...(placement ? {placement} : {}),
$process_person_profile: false, $geoip_disable: true, $ip: '0.0.0.0'
}
id: crypto.randomUUID(), install_id: pageID, consent: true,
event, feature: 'web', outcome: 'success', version: config.version,
...(placement ? { placement } : {})
};
fetch(config.host + '/i/v0/e/', {
fetch(config.host + '/v1/relay/' + config.project, {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload), credentials: 'omit', referrerPolicy: 'no-referrer',
keepalive: true, signal: controller.signal
Expand All @@ -38,7 +35,7 @@
function visit() {
if (landing && choice === 'yes' && !visited && document.visibilityState === 'visible') {
visited = true;
capture('machogs_website_visit');
capture('web.page_view');
}
}

Expand Down Expand Up @@ -69,7 +66,7 @@
});
});
document.querySelectorAll('a[data-download-placement]').forEach(link => {
link.addEventListener('click', () => capture('machogs_download_clicked', link.dataset.downloadPlacement));
link.addEventListener('click', () => capture('web.download_clicked', link.dataset.downloadPlacement));
});
// Consent withdrawn in another tab applies here too.
window.addEventListener('storage', event => {
Expand Down
20 changes: 5 additions & 15 deletions docs/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,8 @@ old-consent isolation, dev silence, payload limits, throttling, opt-out cancella
and identity reset. A real launched-app check and signed/notarized release remain
separate gates before public distribution.

## Website and previously released app

Website collection remains separately optional in docs/analytics.js and
analytics-config.js, using shared PostHog project 39922. Do not change its global
settings. Events machogs_website_visit and machogs_download_clicked use unrelated
random IDs per page load. Person processing and GeoIP enrichment are disabled;
the payload uses the fixed IP placeholder 0.0.0.0. The network provider still sees
the connection IP. Honor Do Not Track and Global Privacy Control.

Released app versions using the prior basic-usage implementation send
machogs_app_first_seen and machogs_app_active_week after consent, once per UTC ISO
week for foreground use. Those versions remain unchanged by this branch.
Dashboard: https://us.posthog.com/project/39922/dashboard/2116324.
Filter older data by product=machogs and environment=production; QA is test.
Download clicks and GitHub asset downloads do not prove installed active users.
## Website and previously released apps

The website uses the same fixed-schema Native Analytics relay after a fresh, separate consent choice. It sends `web.page_view` and `web.download_clicked`, version, feature=web, optional nav/hero/install placement, and an unrelated UUID per page load. It stores no persistent visitor ID and never joins website activity with app IDs. The receiver retains the connection IP for 90 days. Global Privacy Control and Do Not Track disable collection; choosing no cancels pending sends. Only the exact GitHub Pages HTTPS origin is allowed by the relay's browser policy. No collector key is in browser code.

Older released app versions can still send their previous first-use/weekly-use PostHog events until updated. No historical provider data is copied or deleted. Download clicks and GitHub asset downloads do not prove installed active users.
Loading
Loading