Metadata-Version: 2.4
Name: lockin
Version: 0.1.1
Summary: CLI focus blocker for macOS — block distracting websites and apps
Requires-Python: >=3.11
Requires-Dist: psutil>=5.9.0
Requires-Dist: requests>=2.32.5
Requires-Dist: rich>=13.0.0
Requires-Dist: typer>=0.21.1
Description-Content-Type: text/markdown

# Lockin

A CLI focus blocker for macOS. Blocks distracting websites and apps at the network level so you can actually get work done.

- Blocks websites via `/etc/hosts` (not a browser extension — works across all browsers)
- Kills distracting app processes (Discord, Spotify, etc.)
- **Tamper-resistant**: sessions cannot be stopped early, even with `sudo`
- Enforced by a launchd watchdog daemon that re-applies blocks every 3 seconds
- Survives reboots, browser restarts, and DNS flushes

## Install

```bash
pip install lockin
```

Or with [pipx](https://pipx.pypa.io/) (recommended):

```bash
pipx install lockin
```

Requires **macOS** and **Python 3.11+**.

## Quick Start

```bash
# 1. Create a profile
lockin profile create work --preset social --preset entertainment

# 2. Start a focus session (requires sudo for /etc/hosts access)
sudo lockin start work --duration 2h

# 3. Check status
lockin
```

That's it. For the next 2 hours, social media and streaming sites are unreachable and Discord/Spotify get killed on launch.

## Commands

### Sessions

```bash
sudo lockin start <profile> --duration 2h    # Start a focus session
lockin status                                 # Check remaining time (or just `lockin`)
lockin stop                                   # Refused during active sessions (by design)
```

### Profiles

Profiles define what to block. They combine presets with custom sites and apps.

```bash
lockin profile list                           # List all profiles
lockin profile create deep --preset social --preset entertainment --preset news
lockin profile create coding --preset social --site chatgpt.com --app Slack
lockin profile show work                      # See what a profile blocks
lockin profile delete old-profile
```

### Presets

Built-in categories of sites and apps:

```bash
lockin preset                                 # List all presets
```

| Preset | Sites | Apps |
|--------|-------|------|
| **social** | x.com, twitter.com, facebook.com, instagram.com, tiktok.com, reddit.com, threads.net, snapchat.com, linkedin.com | Discord |
| **entertainment** | youtube.com, netflix.com, twitch.tv, hulu.com, disneyplus.com, primevideo.com, spotify.com | Spotify |
| **news** | news.ycombinator.com, cnn.com, bbc.com, nytimes.com, theguardian.com | — |
| **gaming** | steampowered.com, store.steampowered.com, epicgames.com, riotgames.com | Steam, Epic Games Launcher |

Each domain is blocked with all subdomain variants (www, m, api, mobile, app).

### Always-Blocked

Block domains permanently, outside of any session:

```bash
lockin block reddit.com
lockin unblock reddit.com
```

### Schedules

```bash
lockin schedule create mornings --profile work --days mon,tue,wed,thu,fri --start 09:00 --duration 120
lockin schedule list
lockin schedule delete mornings
```

### Other

```bash
lockin apps                                   # List detected macOS apps
sudo lockin install                           # Install watchdog daemon
sudo lockin uninstall                         # Uninstall daemon (only when no active session)
lockin --version
```

## How It Works

1. **Website blocking**: Writes blocked domains to `/etc/hosts` pointing to `0.0.0.0`, then sets the system immutable flag (`chflags schg`) to prevent edits
2. **App blocking**: Kills blocked apps via `osascript` (graceful quit) then `killall` (force kill)
3. **Watchdog daemon**: A launchd daemon (`KeepAlive: true`) runs every 3 seconds to re-apply blocks if tampered with, re-kill blocked apps, and clean up when the session expires
4. **Session signing**: Sessions are signed with HMAC-SHA256 (key derived from hardware UUID). Modifying the session file invalidates the signature

## Tamper Protection

The whole point of Lockin is that you **cannot** bypass it during a session:

| Attack | Mitigation |
|--------|------------|
| Edit `/etc/hosts` | Immutable flag (`schg`) blocks writes; watchdog re-applies if removed |
| Kill the watchdog | launchd auto-respawns it immediately |
| `sudo lockin stop --force` | No code path exists to stop active sessions |
| Delete the session file | Blocks become **permanent** (no valid session = no cleanup) |
| Change the end time in session file | HMAC validation fails, watchdog ignores it |
| Reboot | Session persists at `/var/lockin/`, daemon has `RunAtLoad: true` |
| Change system clock | Watchdog cross-checks elapsed time vs 2x duration |

## Configuration

Profiles and schedules are stored in `~/.config/lockin/config.json`. Sessions are stored in `/var/lockin/session.json`.

## License

MIT
