# Mobile Tailnet Streamlit Web-App Pattern

Use when an existing local Python/Streamlit tool should be usable from iPhone/iPad over Tailscale/Tailnet, especially for local media uploads such as voice notes and videos.

## Implementation checklist

1. **Bind for Tailnet access**
   - Configure Streamlit with `server.address = "0.0.0.0"` and a stable explicit port.
   - Keep the URL Tailnet-only unless the user explicitly asks for public exposure.
   - Verify both `http://127.0.0.1:<port>/` and `http://<tailscale-ip>:<port>/` return HTTP 200.

2. **Mobile upload support**
   - Include iPhone/iPad formats, not only desktop formats:
     - Voice Memos: `.m4a`
     - iPhone/iPad video: `.mov`, `.mp4`, `.m4v`
     - Other useful audio/video: `.wav`, `.mp3`, `.aac`, `.ogg`, `.oga`, `.flac`, `.webm`, `.mkv`, `.avi`
   - Use safe server-side filenames: strip paths, restrict characters, append a short UUID, preserve extension.
   - Set a realistic upload limit for long meetings/videos, e.g. Streamlit `server.maxUploadSize = 4096` for 4 GB when local storage permits.

3. **iOS/iPadOS UX polish**
   - Use large touch targets (`min-height` around 3rem+), rounded buttons, and clear status text.
   - Set text input font-size to at least `16px` to avoid iOS auto-zoom.
   - Collapse multi-column layouts at small widths; Streamlit columns can otherwise become cramped on phones.
   - Provide user-facing instructions for iPhone Voice Memos: Share → Save to Files → upload in app.

4. **Long-running local service**
   - Prefer a user-level systemd service for persistent internal tools:
     - `~/.config/systemd/user/<tool>.service`
     - `WorkingDirectory=<repo>`
     - `ExecStart=<repo>/.venv/bin/streamlit run app.py --server.address 0.0.0.0 --server.port <port> --server.headless true --server.maxUploadSize <mb>`
     - `Restart=on-failure`
   - Enable and start with `systemctl --user enable --now <tool>.service`.
   - Keep repo-local helper scripts for `start`, `stop`, and `status`, but status should also recognize the systemd service as active.

5. **Repo hygiene**
   - Add/confirm `.gitignore` for `.env`, `.venv/`, `input/`, `output/`, `.tmp/`, `__pycache__/`, and Streamlit secrets.
   - Commit only code/config/docs/scripts; never upload user media, generated docs, tokens, or runtime logs.
   - Add placeholder-only `.env.example` with no real secrets.

## Verification commands

Minimum verification before reporting success:

```bash
python -m py_compile app.py <support_modules>.py
scripts/status-webapp.sh
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:<port>/
curl -fsS -o /dev/null -w '%{http_code}\n' http://<tailscale-ip>:<port>/
git diff --check
git status --short --branch
```

For apps with generated outputs, run a cheap mock/smoke path that proves the app can produce an output artifact without launching an expensive full media/LLM job.

## Pitfalls

- Do not claim iPhone support if `.mov` and `.m4a` are missing from allowed upload types.
- Avoid importing modules that hard-exit on missing credentials at page load; raise user-friendly errors only when the user starts processing.
- Avoid full E2E media transcription/generation with large real files unless the user explicitly selects the test file; run mock/syntax/HTTP checks first.
- If normal `git push` lacks credentials but a repo token file exists, use a one-shot credential helper and verify the exact branch ref, not remote HEAD.