# Production Launch Runbook

**Source**: GitHub Issue #135 — Production launch (P1)  
**Scope**: Real PyPI + Stripe live, v0.1.0 tag/release, documentation updates, announcement plan.  
**Do not execute** live PyPI or Stripe actions from this runbook; perform steps manually when ready.

---

## 1. Prerequisites

**All must be true before production launch.**

- [ ] Soft launch feedback collected (#134).
- [ ] No critical bugs reported from soft launch.
- [ ] Stripe webhooks verified with real users (test mode flows confirmed).
- [ ] UI-based E2E flows tested (#131, #132, #133) or accepted as verified.
- [ ] Email verification decision made (if applicable).

**Owner**: Human

---

## 2. Publish to Real PyPI

### 2.1 Build the package (local or CI)

```bash
pip install build twine
python -m build --wheel --outdir dist/
twine check dist/*
```

### 2.2 Environment / credentials

- **Token**: Use a PyPI API token (Account → PyPI → API tokens). Scope: entire account or project `llmhosts`.
- **Env vars** (for `twine upload`):
  - `TWINE_USERNAME`: `__token__`
  - `TWINE_PASSWORD`: your PyPI API token (starts with `pypi-`)
- **Optional**: `TWINE_REPOSITORY_URL` — omit for production PyPI (`https://upload.pypi.org/legacy/`). Use only if you need a different index.

### 2.3 Create `~/.pypirc` (if not using env vars)

```ini
[distutils]
index-servers =
    pypi

[pypi]
username = __token__
password = pypi-YOUR_API_TOKEN_HERE
```

Store token securely; do not commit `.pypirc`. Use `chmod 600 ~/.pypirc`.

### 2.4 Upload to PyPI

```bash
twine upload dist/*
```

Or with env vars only (no `.pypirc`):

```bash
TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-YOUR_TOKEN twine upload dist/*
```

**Owner**: Human (or Automated via CI if GitHub Actions `release` workflow is configured with production `PYPI_API_TOKEN` in environment `release` and tag `v0.1.0` is pushed).

---

## 3. Switch Stripe to Live Mode

### 3.1 In Stripe Dashboard

- Toggle from **Test mode** to **Live mode** (top of dashboard).
- Ensure Live mode products/prices exist (or duplicate from test): Pro, Team, Enterprise with correct live prices ($29 / $99 / $299 or as decided).

### 3.2 Webhook endpoint (Live)

- In Stripe Dashboard → Developers → Webhooks (Live), add or update endpoint:
  - **URL**: `https://llmhosts.com/api/stripe/webhook` (or your production webhook URL).
  - **Events**: `checkout.session.completed`, `customer.subscription.updated`, `customer.subscription.deleted`, `invoice.payment_failed` (match current test setup).
- Copy the **Signing secret** (starts with `whsec_`).

### 3.3 Application environment

- Set production env to use live key and live webhook secret:
  - `STRIPE_SECRET_KEY`: Live secret key (starts with `sk_live_`).
  - `STRIPE_WEBHOOK_SECRET`: Live webhook signing secret (`whsec_...`).
- Remove or override any test keys in production.

**Owner**: Human

---

## 4. Update Documentation

Update the following so no references point to TestPyPI or test-only flows.

| File / Location | Change |
|-----------------|--------|
| **README.md** | PyPI badge URL: `https://test.pypi.org/project/llmhosts/` → `https://pypi.org/project/llmhosts/`. Install line: `pip install llmhosts` (no `--index-url`). |
| **docs/LAUNCH_STATUS.md** | Set status to Production; replace TestPyPI URLs with pypi.org; document Stripe live and real PyPI. |
| **CHANGELOG.md** | If it says "TestPyPI", add a line that v0.1.0 is on real PyPI as of [date]. |
| **docs/SOFT_LAUNCH_ANNOUNCEMENT.md** (if kept) | Add note "Superseded by production; install from PyPI." or remove TestPyPI-only install. |
| **Any other docs** | Search repo for `test.pypi.org`, `TestPyPI`, `--index-url https://test.pypi.org` and replace with production PyPI / `pip install llmhosts` as appropriate. |

**Owner**: Human (or Automated via script that does find/replace and PR).

---

## 5. Create Git Tag and GitHub Release

### 5.1 Tag

```bash
git tag -a v0.1.0 -m "Release v0.1.0 — production launch"
git push origin v0.1.0
```

### 5.2 GitHub Release

- Either rely on CI: if `.github/workflows/release.yml` runs on tag push, it will create the release and attach the wheel (and may publish to PyPI if `PYPI_API_TOKEN` is set for the `release` environment).
- Or create manually: Repo → Releases → Draft a new release → Choose tag `v0.1.0` → Use CHANGELOG snippet for v0.1.0 as body → Publish.

**Owner**: Human (tag push); Automated (GitHub Actions for build + release creation; PyPI publish depends on secret config).

---

## 6. Announcement Plan

| Channel | Action | Owner |
|---------|--------|--------|
| **Hacker News** | "Show HN" post with title and link to repo or llmhosts.com; one sentence on what it is and why it’s useful. | Human |
| **Reddit** | r/selfhosted, r/LocalLLaMA, r/Python — production launch post (real PyPI, live Stripe); follow each sub’s rules. | Human |
| **Product Hunt** | Schedule or publish launch; one sentence pitch + link. | Human |
| **Twitter/X** | Thread with demo or short video; link to llmhosts.com and PyPI. | Human |
| **Email** | Email interested users / waiting list with production link and `pip install llmhosts`. | Human |

---

## Summary: Human vs Automated

| Step | Owner |
|------|--------|
| Prerequisites (feedback, bugs, Stripe verification) | Human |
| Build wheel + `twine check` | Automated (CI) or Human |
| Upload to real PyPI | Human (or Automated if CI has production token) |
| Stripe live mode + webhook + env | Human |
| Doc updates (README, LAUNCH_STATUS, etc.) | Human |
| Git tag `v0.1.0` + push | Human |
| GitHub Release | Automated (CI on tag) or Human |
| Announcements (HN, Reddit, PH, Twitter, email) | Human |
