Backend - dev-api.py: Q&A suggest endpoint, Log Contact, cf-orch node detection in wizard hardware step, canonical search_profiles format (profiles:[...]), connections settings endpoints, Resume Library endpoints - db_migrate.py: migrations 002/003/004 — ATS columns, resume review, final resume struct - discover.py: _normalize_profiles() for legacy wizard YAML format compat - resume_optimizer.py: section-by-section resume parsing + scoring - task_runner.py: Q&A and contact-log task types - company_research.py: accessibility brief column wiring - generate_cover_letter.py: restore _candidate module-level binding Frontend - InterviewPrepView.vue: Q&A chat tab, Log Contact form, MarkdownView rendering - InterviewCard.vue: new reusable card component for interviews kanban - InterviewsView.vue: rejected analytics section with stage breakdown chips - ResumeProfileView.vue: sync with new resume store shape - SearchPrefsView.vue: cf-orch toggle, profile format migration - SystemSettingsView.vue: connections settings wiring - ConnectionsSettingsView.vue: new view for integration connections - MarkdownView.vue: new component for safe markdown rendering - ApplyWorkspace.vue: a11y — h1→h2 demotion, aria-expanded on Q&A toggle, confirmation dialog on Reject action (#98 #99 #100) - peregrine.css: explicit [data-theme="dark"] token block for light-OS users (#101), :focus-visible outline (#97) - wizard.css: cf-orch hardware step styles - WizardHardwareStep.vue: cf-orch node display, profile selection with orch option - WizardLayout.vue: hardware step wiring Infra - compose.yml / compose.cloud.yml: cf-orch agent sidecar, llm.cloud.yaml mount - Dockerfile.cfcore: cf-core editable install in image build - HANDOFF-xanderland.md: Podman/systemd setup guide for beta tester - podman-standalone.sh: standalone Podman run script Tests - test_dev_api_settings.py: remove stale worktree path bootstrap (credential_store now in main repo); fix job_boards fixture to use non-empty list - test_wizard_api.py: update profiles assertion to superset check (cf-orch added); update step6 assertion to canonical profiles[].titles format
153 lines
3.9 KiB
Markdown
153 lines
3.9 KiB
Markdown
# Peregrine → xanderland.tv Setup Handoff
|
|
|
|
**Written from:** dev machine (CircuitForge dev env)
|
|
**Target:** xanderland.tv (beta tester, rootful Podman + systemd)
|
|
**Date:** 2026-02-27
|
|
|
|
---
|
|
|
|
## What we're doing
|
|
|
|
Getting Peregrine running on the beta tester's server as a Podman container managed by systemd. He already runs SearXNG and other services in the same style — rootful Podman with `--net=host`, `--restart=unless-stopped`, registered as systemd units.
|
|
|
|
The script `podman-standalone.sh` in the repo root handles the container setup.
|
|
|
|
---
|
|
|
|
## Step 1 — Get the repo onto xanderland.tv
|
|
|
|
From navi (or directly if you have a route):
|
|
|
|
```bash
|
|
ssh xanderland.tv "sudo git clone <repo-url> /opt/peregrine"
|
|
```
|
|
|
|
Or if it's already there, just pull:
|
|
|
|
```bash
|
|
ssh xanderland.tv "cd /opt/peregrine && sudo git pull"
|
|
```
|
|
|
|
---
|
|
|
|
## Step 2 — Verify /opt/peregrine looks right
|
|
|
|
```bash
|
|
ssh xanderland.tv "ls /opt/peregrine"
|
|
```
|
|
|
|
Expect to see: `Dockerfile`, `compose.yml`, `manage.sh`, `podman-standalone.sh`, `config/`, `app/`, `scripts/`, etc.
|
|
|
|
---
|
|
|
|
## Step 3 — Config
|
|
|
|
```bash
|
|
ssh xanderland.tv
|
|
cd /opt/peregrine
|
|
sudo mkdir -p data
|
|
sudo cp config/llm.yaml.example config/llm.yaml
|
|
sudo cp config/notion.yaml.example config/notion.yaml # only if he wants Notion sync
|
|
```
|
|
|
|
Then edit `config/llm.yaml` and set `searxng_url` to his existing SearXNG instance
|
|
(default is `http://localhost:8888` — confirm his actual port).
|
|
|
|
He won't need Anthropic/OpenAI keys to start — the setup wizard lets him pick local Ollama
|
|
or whatever he has running.
|
|
|
|
---
|
|
|
|
## Step 4 — Fix DOCS_DIR in the script
|
|
|
|
The script defaults `DOCS_DIR=/Library/Documents/JobSearch` which is the original user's path.
|
|
Update it to wherever his job search documents actually live, or a placeholder empty dir:
|
|
|
|
```bash
|
|
sudo mkdir -p /opt/peregrine/docs # placeholder if he has no docs yet
|
|
```
|
|
|
|
Then edit the script:
|
|
```bash
|
|
sudo sed -i 's|DOCS_DIR=.*|DOCS_DIR=/opt/peregrine/docs|' /opt/peregrine/podman-standalone.sh
|
|
```
|
|
|
|
---
|
|
|
|
## Step 5 — Build the image
|
|
|
|
```bash
|
|
ssh xanderland.tv "cd /opt/peregrine && sudo podman build -t localhost/peregrine:latest ."
|
|
```
|
|
|
|
Takes a few minutes on first run (downloads python:3.11-slim, installs deps).
|
|
|
|
---
|
|
|
|
## Step 6 — Run the script
|
|
|
|
```bash
|
|
ssh xanderland.tv "sudo bash /opt/peregrine/podman-standalone.sh"
|
|
```
|
|
|
|
This starts a single container (`peregrine`) with `--net=host` and `--restart=unless-stopped`.
|
|
SearXNG is NOT included — his existing instance is used.
|
|
|
|
Verify it came up:
|
|
```bash
|
|
ssh xanderland.tv "sudo podman ps | grep peregrine"
|
|
ssh xanderland.tv "sudo podman logs peregrine"
|
|
```
|
|
|
|
Health check endpoint: `http://xanderland.tv:8501/_stcore/health`
|
|
|
|
---
|
|
|
|
## Step 7 — Register as a systemd service
|
|
|
|
```bash
|
|
ssh xanderland.tv
|
|
sudo podman generate systemd --new --name peregrine \
|
|
| sudo tee /etc/systemd/system/peregrine.service
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now peregrine
|
|
```
|
|
|
|
Confirm:
|
|
```bash
|
|
sudo systemctl status peregrine
|
|
```
|
|
|
|
---
|
|
|
|
## Step 8 — First-run wizard
|
|
|
|
Open `http://xanderland.tv:8501` in a browser.
|
|
|
|
The setup wizard (page 0) will gate the app until `config/user.yaml` is created.
|
|
He'll fill in his profile — name, resume, LLM backend preferences. This writes
|
|
`config/user.yaml` and unlocks the rest of the UI.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | Check |
|
|
|---------|-------|
|
|
| Container exits immediately | `sudo podman logs peregrine` — usually a missing config file |
|
|
| Port 8501 already in use | `sudo ss -tlnp \| grep 8501` — something else on that port |
|
|
| SearXNG not reachable | Confirm `searxng_url` in `config/llm.yaml` and that JSON format is enabled in SearXNG settings |
|
|
| Wizard loops / won't save | `config/` volume mount permissions — `sudo chown -R 1000:1000 /opt/peregrine/config` |
|
|
|
|
---
|
|
|
|
## To update Peregrine later
|
|
|
|
```bash
|
|
cd /opt/peregrine
|
|
sudo git pull
|
|
sudo podman build -t localhost/peregrine:latest .
|
|
sudo podman restart peregrine
|
|
```
|
|
|
|
No need to touch the systemd unit — it launches fresh via `--new` in the generate step.
|