Guide for new developers to get all APIs and services working.
- macOS (Apple Silicon)
- Xcode 16+
- Node.js 18+ (for Cloudflare Workers)
- Ruby 3+ (for build scripts)
- Wrangler CLI (
npm i -g wrangler) - GitHub CLI (
brew install gh)
When any new local tool is installed for build/release/test work, record it here immediately.
| Date (YYYY-MM-DD) | Machine | Tool | Version | Install command | Why installed | Verification command |
|---|---|---|---|---|---|---|
| add row | mini / air |
name | x.y.z | exact command | blocking issue or workflow need | exact check command |
What it does: Hosts all websites (Pages), distribution workers (R2 + Workers), email automation, analytics.
| Resource | Purpose |
|---|---|
| Workers | Download gating (sane-dist), email automation, click tracking, redirects |
| R2 Buckets | Shared distribution bucket (sanebar-downloads) for all SaneApps (.dmg/.zip) |
| D1 Database | Email/customer storage |
| KV Namespace | Email caching |
| Pages | Product websites (sanebar.com, saneclick.com, etc.) |
| Email Routing | hi@saneapps.com → Worker |
Setup:
# Login to Cloudflare
npx wrangler login
# Verify access
npx wrangler whoamiAPI token (ask owner for token with these permissions):
- Account: Workers Scripts, R2, D1, KV — Edit
- Zone: DNS, Workers Routes — Edit
- All zones in account
Store in keychain:
security add-generic-password -s cloudflare -a api_token -w "YOUR_TOKEN"What it does: Code signing, notarization, App Store Connect (for Fastlane).
| Credential | Value |
|---|---|
| Team ID | M78L6FXD48 |
| Signing Identity | Developer ID Application (Team: M78L6FXD48) |
| Primary API Key ID | S34998ZCRT (SaneApps — Admin access) |
| Issuer ID | c98b1e0a-8d10-4fce-a417-536b31c09bfb |
| .p8 Location | ~/.private_keys/AuthKey_S34998ZCRT.p8 |
Setup:
- Get invited to the Apple Developer team
- Install signing certificate in Keychain Access
- Store notarization profile:
xcrun notarytool store-credentials "notarytool" \
--key ~/.private_keys/AuthKey_S34998ZCRT.p8 \
--key-id S34998ZCRT \
--issuer c98b1e0a-8d10-4fce-a417-536b31c09bfb- Copy
.p8file from owner to~/.private_keys/AuthKey_S34998ZCRT.p8(chmod 600)
Headless mini release requirements (SSH/non-interactive):
export NOTARY_API_KEY_PATH="$HOME/.private_keys/AuthKey_S34998ZCRT.p8"
export NOTARY_API_KEY_ID="S34998ZCRT"
export NOTARY_API_ISSUER_ID="c98b1e0a-8d10-4fce-a417-536b31c09bfb"
export SANEBAR_KEYCHAIN_PASSWORD="<your-login-keychain-password>"
# Validate all release gates before building/publishing:
cd ~/SaneApps/infra/SaneProcess/scripts
./release.sh --project ~/SaneApps/apps/SaneHosts --preflight-only --allow-unsynced-peer --version 1.0.9If preflight reports Codesign cannot access signing key, the keychain password env is missing/invalid.
What it does: In-app update mechanism for all macOS apps.
ONE shared EdDSA key for ALL SaneApps.
| Item | Value |
|---|---|
| Public key (SUPublicEDKey) | 7Pl/8cwfb2vm4Dm65AByslkMCScLJ9tbGlwGGx81qYU= |
| Private key location | macOS Keychain, account: EdDSA Private Key |
Setup: Ask owner to export the Sparkle private key. Import it:
# The key is stored under the Sparkle keychain service
# Owner will provide the base64 private key to importNEVER run generate_keys — that creates a new keypair and breaks updates for shipped versions.
What it does: Payment processing, license keys, checkout pages.
| Item | Detail |
|---|---|
| Store | saneapps.lemonsqueezy.com |
| Checkout URLs | Via go.saneapps.com redirect Worker |
Setup:
# Store API key in keychain
security add-generic-password -s lemonsqueezy -a api_key -w "YOUR_KEY"
# For Cloudflare Worker (email automation)
cd ~/SaneApps/infra/sane-email-automation
npx wrangler secret put LEMONSQUEEZY_API_KEY
npx wrangler secret put LEMONSQUEEZY_WEBHOOK_SECRETWhat it does: Sends emails from hi@saneapps.com, handles transactional email.
Setup:
# Store API key in keychain
security add-generic-password -s resend -a api_key -w "YOUR_KEY"
# For Cloudflare Worker
npx wrangler secret put RESEND_API_KEYDomain saneapps.com is already verified in Resend.
What it does: Source code, issues, releases, CI.
| Item | Detail |
|---|---|
| Org | sane-apps |
| Repos | SaneBar, SaneClick, SaneClip, SaneHosts, SaneSync, SaneVideo, sane-email-automation |
Setup:
gh auth login
# For Cloudflare Worker (issue creation from emails)
npx wrangler secret put GITHUB_TOKENWhat it does: Receives hi@saneapps.com, AI-categorizes, auto-responds, creates GitHub issues.
All /api/* endpoints require bearer token auth.
Setup (after getting access to Cloudflare):
cd ~/SaneApps/infra/sane-email-automation
npm install
# Set all secrets
npx wrangler secret put API_KEY
npx wrangler secret put RESEND_API_KEY
npx wrangler secret put GITHUB_TOKEN
npx wrangler secret put LEMONSQUEEZY_API_KEY
npx wrangler secret put LEMONSQUEEZY_WEBHOOK_SECRET
npx wrangler secret put DOWNLOAD_SIGNING_SECRET
# Deploy
npx wrangler deployAPI key for local testing (ask owner, stored in keychain as sane-email-automation / api_key).
What it does: Signed URL download system. Customers get time-limited links to DMGs on R2.
Each app has a dist worker at dist.{appname}.com with a shared signing secret.
Setup:
# Store signing secret in keychain
security add-generic-password -s sanebar-dist -a signing_secret -w "YOUR_SECRET"The signing secret must match the SIGNING_SECRET Worker secret on Cloudflare.
What it does: Social media posting via API.
Setup:
security add-generic-password -s x-api -a consumer_key -w "KEY"
security add-generic-password -s x-api -a consumer_secret -w "SECRET"
security add-generic-password -s x-api -a access_token -w "TOKEN"
security add-generic-password -s x-api -a access_token_secret -w "SECRET"| Domain | Purpose | Hosting |
|---|---|---|
| sanebar.com | Product site + appcast | Cloudflare Pages |
| saneclick.com | Product site + appcast | Cloudflare Pages |
| saneclip.com | Product site + appcast | Cloudflare Pages |
| sanehosts.com | Product site + appcast | Cloudflare Pages |
| sanesync.com | Product site | Cloudflare Pages |
| sanevideo.com | Product site | Cloudflare Pages |
| saneapps.com | Main brand site + email | Cloudflare Pages |
| dist.*.com | Download gating | Cloudflare Workers + R2 |
| go.saneapps.com | Checkout redirects | Cloudflare Worker |
| email-api.saneapps.com | Email automation API | Cloudflare Worker |
After setup, verify everything works:
# Cloudflare
npx wrangler whoami
# GitHub
gh auth status
# Apple signing
security find-identity -v -p codesigning | grep "Developer ID"
# Notarization
xcrun notarytool history --keychain-profile "notarytool" | head -5
# Build SaneBar
cd ~/SaneApps/apps/SaneBar
./scripts/SaneMaster.rb verify- NEVER run Sparkle
generate_keys— breaks updates for shipped versions - NEVER commit secrets to git — use keychain or
wrangler secret put - NEVER use GitHub Releases for DMGs — use Cloudflare R2 via dist.{app}.com
- NEVER create Homebrew formulas — distribution is Cloudflare R2 + Sparkle only