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 CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ If you're an AI agent (Claude Code, Codex, etc.) working in this repo, read this
| Add a new alias | `config/aliases.sh` (or `aliases_<name>.sh` for env-specific) |
| Add a deploy component | Create `deploy_X()` in `deploy.sh` — see [Adding New Features](#adding-new-features) |
| Add a custom binary | Drop it in `custom_bins/` (already on PATH); `chmod +x` |
| Install/manage Mac apps | Add a line to `config/apps.conf` → run `app-picker` (gum TUI) → `brew bundle --file=config/Brewfile`. Official casks + `mas` only, **no third-party taps**. Then `scripts/setup/auth-setup` |
| Add an encrypted secret | `secrets-edit` (interactive dotenv editor) |
| Run an experiment with resource caps | `jexp uv run python -m ...` (Linux: needs pueue + systemd user session) |
| Commit / commit + push + PR | `/commit` skill or `/commit-push-sync` |
Expand Down
26 changes: 26 additions & 0 deletions claude/rules/supply-chain-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,32 @@ All package managers are configured with a **7-day quarantine** (`min-release-ag
- Skip hash verification for production Python dependencies
- Bypass min-release-age quarantine without explicit user approval

## GUI Apps & Brewfile (macOS)

Apps live in `config/apps.conf` (registry) → `config/Brewfile` (generated by `app-picker`).

- **Homebrew official casks + Mac App Store (`mas`) ONLY.** NEVER add a third-party tap
to `apps.conf`, install.sh, or a Brewfile without explicit user approval.
- **Prefer `mas`** (sandboxed, Apple-reviewed) when an app ships full-featured on the
App Store — highest trust tier ("MAS-first"). Use a cask when MAS is crippled/absent.
- Before adding any app: run `brew info <cask>` / `mas info <id>`, verify vendor + homepage.
- **Never `--no-quarantine`.** Gatekeeper + notarization must stay enabled; that's the
defense against malicious casks (brew also verifies a pinned sha256 on download).
- Tier in `apps.conf`: 1 = official vendor auto-approve · 2 = mature OSS (review) ·
3 = explicit approval (ships `default=false`).

## curl|bash Installers

Official-page `curl … | sh` gives **authenticity** (HTTPS proves the domain) but NOT
**integrity** (runs whatever's live, unpinned, unreviewed). Prefer, best→worst:
1. Official Homebrew **formula** if one exists (`uv`, `rustup-init`, `bun`) — vendor's
artifact + sha pin + reviewed PR + reproducible.
2. No formula → `curl -o` a versioned URL and **verify the vendor checksum/signature**.
3. Blind `curl … | sh` only as last resort, HTTPS-to-official-domain only.

Eyeballing the script ("glance at it") is a smell test for gross tampering, NOT an
integrity control — don't treat it as a safeguard.

## Secrets Awareness

- API keys are scoped per-project via direnv `.envrc`, NOT globally exported
Expand Down
6 changes: 3 additions & 3 deletions config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ INSTALL_REGISTRY=(
"ai-tools|Claude Code, Gemini CLI, Codex CLI|all|true"
"extras|hyperfine, gitui, code2prompt, terminal-notifier|all|true"
"cleanup|Automatic cleanup (macOS only)|all|true"
"experimental|ty type checker, zerobrew|all|true"
"experimental|ty type checker, zotero MCP|all|true"
"macos-settings|macOS system defaults (Dock, Finder, keyboard)|macos|true"
"finicky|Finicky browser routing|macos|true"
"apps|GUI + App Store apps via Brewfile (picker TUI)|macos|true"
"docker|Docker engine + compose|linux|true"
"pueue|Pueue job scheduler + pueued daemon|linux|true"
"create-user|Create non-root dev user|linux|true"
Expand Down Expand Up @@ -230,7 +230,7 @@ apply_profile() {
INSTALL_DOCKER=false
INSTALL_EXTRAS=false
INSTALL_MACOS_SETTINGS=false
INSTALL_FINICKY=false
INSTALL_APPS=false
DEPLOY_EDITOR=false
DEPLOY_SERENA=false
DEPLOY_GHOSTTY=false
Expand Down
43 changes: 43 additions & 0 deletions config/Brewfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Brewfile — GENERATED by app-picker from config/apps.conf. Do not edit by hand.
# Regenerate: app-picker (or app-picker --defaults)
# Install: brew bundle --file=config/Brewfile
# Policy: official casks + Mac App Store only; never --no-quarantine.

# mas-cli drives Mac App Store installs (must be signed into the App Store).
brew "mas"

# ── Casks (official Homebrew casks) ──
cask "aldente"
cask "alfred"
cask "antigravity"
cask "appcleaner"
cask "beardedspice"
cask "chatgpt"
cask "claude"
cask "cleanshot"
cask "cursor"
cask "dropbox"
cask "finicky"
cask "ghostty"
cask "granola"
cask "keyboardcleantool"
cask "mouseless"
cask "nordvpn"
cask "notion"
cask "popclip"
cask "readdle-spark"
cask "slack"
cask "spotify"
cask "stats"
cask "super-productivity"
cask "tailscale-app"
cask "voiceink"
cask "zed"

# ── Mac App Store (sandboxed, Apple-reviewed) ──
mas "2FAS Auth Browser Extension", id: 6443941139
mas "Bear", id: 1091189122
mas "Bitwarden", id: 1352778147
mas "Things 3", id: 904280696
mas "Userscripts", id: 1463298887
mas "uBlock Origin Lite", id: 6745342698
15 changes: 0 additions & 15 deletions config/aliases.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1322,21 +1322,6 @@ alias ai-update='update-ai-tools'
# Detects: brew (macOS), apt/dnf/pacman (Linux)
alias pkg-update='update-packages'

# zerobrew: faster Homebrew client (use zb for interactive installs, brew for scripts)
# `zb install` falls back to `brew install` on failure (zerobrew doesn't handle casks)
if command -v zb &>/dev/null; then
zb() {
if [[ "$1" == "install" ]]; then
shift
command zb install "$@" || { echo "→ zb failed, falling back to brew install" >&2; brew install "$@"; }
else
command zb "$@"
fi
}
alias zbi='zb install'
alias zbu='zb uninstall'
fi

# Auto-agent guard controls
alias auto-guard='auto-agent-guardctl status'
alias auto-approve='auto-agent-guardctl approve'
Expand Down
115 changes: 115 additions & 0 deletions config/apps.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# ═══════════════════════════════════════════════════════════════════════════════
# App Registry — single source of truth for GUI apps + App Store apps
# ═══════════════════════════════════════════════════════════════════════════════
# Consumed by: custom_bins/app-picker (gum toggle TUI → generates config/Brewfile)
# Installed by: brew bundle --file=config/Brewfile (install.sh --apps)
#
# Format (pipe-delimited, one app per line):
# method | id | category | tier | default | name | description | auth
#
# method brew | cask | mas
# id formula name (brew) / cask token (cask) / numeric App Store id (mas)
# category text tasks editor llm cli meetings cloud search messaging
# productivity time voice audio vpn auth safari music misc antivirus security
# tier 1 = official-vendor auto-approve · 2 = mature OSS (review) · 3 = explicit approval
# (tier-3 ships default=false; toggle on deliberately)
# default true | false — initial toggle state in the picker
# name display name; for `mas` this is the exact App Store title in the Brewfile
# auth post-install setup the auth-setup checklist surfaces:
# none login apikey pair-phone safari-ext license alfred things-cloud
#
# SECURITY POLICY (see claude/rules/supply-chain-security.md):
# - Homebrew official casks + Mac App Store only. NEVER add a third-party tap here.
# - Prefer `mas` (sandboxed, Apple-reviewed) when an app ships full-featured on the
# App Store — that's the highest trust tier ("MAS-first").
# - Run `brew info <cask>` / `mas info <id>` and verify vendor+homepage before adding.
# - Never use --no-quarantine; Gatekeeper/notarization must stay on.
# ═══════════════════════════════════════════════════════════════════════════════

# ─── text ───────────────────────────────────────────────────────────────────
mas|1091189122|text|2|true|Bear|Markdown notes (App Store only; bearcli symlinked by deploy)|login
cask|notion|text|1|true|Notion|Notes, docs, and wiki|login

# ─── task management ─────────────────────────────────────────────────────────
mas|904280696|tasks|2|true|Things 3|GTD task manager (App Store only)|things-cloud

# ─── coding: editors ─────────────────────────────────────────────────────────
cask|cursor|editor|1|true|Cursor|AI-first code editor (config deployed)|login
cask|antigravity|editor|1|true|Antigravity|Google agentic IDE (config deployed)|login
cask|zed|editor|1|true|Zed|Fast collaborative editor (config deployed)|login

# ─── coding: LLM desktop apps ────────────────────────────────────────────────
cask|chatgpt|llm|1|true|ChatGPT|OpenAI desktop app|login
cask|claude|llm|1|true|Claude|Anthropic desktop app|login

# ─── coding: terminal ────────────────────────────────────────────────────────
cask|ghostty|cli|1|true|Ghostty|GPU-accelerated terminal (config deployed)|none

# ─── meetings ────────────────────────────────────────────────────────────────
cask|granola|meetings|2|true|Granola|AI meeting notes|login

# ─── cloud storage ───────────────────────────────────────────────────────────
cask|dropbox|cloud|1|true|Dropbox|File sync and storage|login
cask|google-drive|cloud|1|false|Google Drive|Google cloud storage (optional)|login

# ─── search / launcher ───────────────────────────────────────────────────────
cask|alfred|search|2|true|Alfred|Launcher + workflows (prefs sync via Dropbox)|alfred

# ─── messaging ───────────────────────────────────────────────────────────────
cask|slack|messaging|1|true|Slack|Team messaging|login
cask|readdle-spark|messaging|2|true|Spark|Email client (MAS build also full-featured)|login

# ─── productivity ────────────────────────────────────────────────────────────
cask|mouseless|productivity|2|true|Mouseless|Keyboard-driven mouse control (config deployed)|none
cask|popclip|productivity|2|true|PopClip|Text-selection actions (cask not MAS — MAS abandoned)|license

# ─── time tracking ───────────────────────────────────────────────────────────
cask|super-productivity|time|2|true|Super Productivity|Task + time tracker|none
brew|wakatime-cli|time|2|false|WakaTime CLI|Coding time tracker (API key via secrets)|apikey

# ─── voice ───────────────────────────────────────────────────────────────────
cask|voiceink|voice|2|true|VoiceInk|Local voice-to-text (downloads model on first run)|none

# ─── audio device management ─────────────────────────────────────────────────
# FineTune: per-app volume + multi-device output/routing + EQ (free OSS SoundSource alt).
# Default OFF: newer + single-maintainer project — adoption is high but bus-factor
# risk remains, so it's a conscious opt-in (see supply-chain-security.md modernity rule).
cask|finetune|audio|2|false|FineTune|Per-app volume + audio device routing + EQ (OSS)|none
# switchaudio-osx: CLI to switch the SYSTEM default input/output device. Core formula,
# OSS, no audio driver. Pairs with an Alfred keyword: SwitchAudioSource -t output -s "AirPods".
brew|switchaudio-osx|audio|2|false|SwitchAudioSource|CLI default input/output device switcher (Alfred-friendly)|none

# ─── vpn ─────────────────────────────────────────────────────────────────────
cask|nordvpn|vpn|2|true|NordVPN|VPN client (vpn deploy configures split tunnel)|login
cask|tailscale-app|vpn|1|true|Tailscale|Mesh VPN (vpn deploy configures routing)|login

# ─── auth / passwords / 2FA ──────────────────────────────────────────────────
# Bitwarden via MAS: the Safari extension ships only in the App Store build, which
# also covers the desktop app — so mas covers both. (cask `bitwarden` = desktop only.)
mas|1352778147|auth|1|true|Bitwarden|Password manager (App Store build incl. Safari ext)|login
mas|6443941139|auth|2|true|2FAS Auth Browser Extension|2FA browser extension (pairs with phone)|pair-phone

# ─── safari extensions (install via mas; ENABLE manually in Safari settings) ──
mas|6745342698|safari|2|true|uBlock Origin Lite|Content blocker (enable in Safari)|safari-ext
mas|1463298887|safari|2|true|Userscripts|Userscript manager (enable in Safari)|safari-ext

# ─── music ───────────────────────────────────────────────────────────────────
cask|spotify|music|1|true|Spotify|Music streaming|login

# ─── misc utilities ──────────────────────────────────────────────────────────
cask|aldente|misc|2|true|AlDente|Battery charge limiter|none
cask|finicky|misc|2|true|Finicky|Browser routing (config deployed)|none
cask|appcleaner|misc|2|true|AppCleaner|Thorough app uninstaller|none
cask|cleanshot|misc|1|true|CleanShot X|Screenshot + screen recording|license
cask|stats|misc|2|true|Stats|Menu-bar system monitor|none
cask|keyboardcleantool|misc|2|true|KeyboardCleanTool|Disable keyboard for cleaning|none
cask|beardedspice|misc|2|true|BeardedSpice|Media keys for web players|none

# ─── antivirus ───────────────────────────────────────────────────────────────
# Optional, default OFF. Trellix (university-managed) is NOT here — installed via uni.
# Do NOT run Malwarebytes real-time protection alongside Trellix (on-demand scan is fine).
cask|malwarebytes|antivirus|2|false|Malwarebytes|On-demand malware scanner (optional)|login

# ─── security: runtime defense ───────────────────────────────────────────────
# Optional, default OFF (prompts frequently). Objective-See outbound firewall.
cask|lulu|security|2|false|LuLu|Outbound firewall — catches apps phoning home|none
8 changes: 0 additions & 8 deletions config/experimental.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,3 @@
settings_keys: []
reason: "Rust-based Python type checker, 10-60x faster than mypy/pyright. Alpha — fall back to pyright if gaps block."
status: trial

- name: zerobrew
added: 2026-04-01
review_by: 2026-07-01
installed_via: "curl https://zerobrew.rs/install | bash"
settings_keys: []
reason: "Fast Rust-based Homebrew client. Same UX, faster."
status: trial
31 changes: 31 additions & 0 deletions config/login_items.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ═══════════════════════════════════════════════════════════════════════════════
# login_items.conf — apps to register as macOS "Open at Login" items
# ═══════════════════════════════════════════════════════════════════════════════
# Consumed by: scripts/setup/setup-login-items
#
# Why a separate list (not apps.conf): "launch at login" is orthogonal to install.
# apps.conf decides WHICH apps to install; this decides which menu-bar apps should
# auto-start so they're reliably present in the menu bar.
#
# Format (pipe-delimited, one app per line):
# name [| hidden]
# name exact app name in /Applications (without ".app"); must match the
# name macOS shows in System Settings → Login Items
# hidden true | false — start hidden (no window). Default true, which is
# right for menu-bar utilities. Set false for apps you want to open.
#
# ── How customising works (read this — it answers "do I have to edit dotfiles?") ─
# NO. Day-to-day, just toggle apps in System Settings → General → Login Items.
# setup-login-items is BOOTSTRAP-ONCE + ADDITIVE:
# • it only ADDS a curated app if it's missing AND it hasn't added it before
# • it NEVER removes anything, and NEVER re-adds something you removed manually
# (it records what it added in ~/.config/dotfiles/login-items.bootstrapped)
# • it does NOT run on every deploy — you invoke it (or it's part of app setup)
# This list exists only to SEED a fresh machine's menu bar. Edit it only to change
# what a brand-new install should auto-add.
# ═══════════════════════════════════════════════════════════════════════════════

Stats
FineTune
Tailscale
NordVPN
9 changes: 9 additions & 0 deletions config/macos_settings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ configure_mouse() {
configure_dock() {
echo " → Configuring Dock..."

# Auto-hide the Dock
defaults write com.apple.dock autohide -bool true 2>/dev/null || true

# No delay before the Dock appears on hover (instant show)
defaults write com.apple.dock autohide-delay -float 0 2>/dev/null || true

# Conservative icon size
defaults write com.apple.dock tilesize -int 48 2>/dev/null || true

# Hide recent apps section
defaults write com.apple.dock show-recents -bool false 2>/dev/null || true

Expand Down
Loading
Loading