# FOY Design-to-Build Docker Container

A self-contained design-to-manufacturing system for hiking insoles. Runs in a single Docker container on any cloud host or local machine.

## Quick Start

```bash
docker run -d \
  --name foy-system \
  -p 3000:3000 \
  -p 8000:8000 \
  -v foy-data:/data \
  --restart unless-stopped \
  ghcr.io/deboboy/foy-system:latest
```

Open **http://127.0.0.1:3000** in your browser (on Docker Desktop / macOS this is often more reliable than `http://localhost:3000`, which may prefer IPv6).

## System Requirements

**Measured from live container:** ~137 MB RAM idle, <1% CPU, 676 MB image size.

### Minimum (runs but tight)

| Resource | Minimum | Notes |
|----------|---------|-------|
| RAM | **512 MB** | Container uses ~137 MB idle; enough headroom for DXF generation |
| CPU | **1 vCPU** | DXF generation takes ~0.5s on a single core |
| Disk | **3 GB** | Image ~676 MB + ~1 GB working space for Docker + growing data |
| OS | Linux (arm64 or amd64) | Docker Engine 20.10+ required |

### Recommended (for comfortable operation)

| Resource | Recommended | Notes |
|----------|-------------|-------|
| RAM | **1 GB** | Extra headroom for cache, concurrent brief processing |
| CPU | **1-2 vCPUs** | Faster DXF generation, smoother UI |
| Disk | **5 GB** | Room for 100+ projects with DXF + BOM files |

### Hetzner Example Tiers

| Instance | Arch | RAM | CPU | Disk | Cost/mo | Verdict |
|----------|------|-----|-----|------|---------|---------|
| CAX11 | ARM64 | 4 GB | 2 | 40 GB | ~EUR 3.29 | Best fit, plenty of headroom |
| CPX11 | ARM64 | 2 GB | 2 | 40 GB | ~EUR 4.51 | Great, no issues |
| CX11 | x86_64 | 2 GB | 1 | 20 GB | ~EUR 3.29 | Works, rebuild with `--platform linux/amd64` |

> **Note**: Current image is built for ARM64 (aarch64). For x86_64 hosts (Hetzner CX), rebuild with `docker build --platform linux/amd64 -t foy-system .` or add `--platform` flag to the Dockerfile's FROM line.

## Access Points
- **API root** (`GET /`): http://localhost:8000/ — short JSON discovery (not the Next.js UI)
- **UI**: http://127.0.0.1:3000 (or `http://localhost:3000` if IPv4/IPv6 routing matches your setup)
- **API Docs**: http://localhost:8000/docs
- **Health Check**: http://localhost:8000/health
- **Hermes diagnostics**: http://localhost:8000/api/hermes/status
- **Hermes web chat**: Next.js **`/chat`** → `POST /api/hermes/chat` (Hermes `AIAgent` + OpenRouter when `OPENROUTER_API_KEY` or `HERMES_LLM_API_KEY` is set; otherwise stub). See [`FOY_OPERATING_MODES.md`](./FOY_OPERATING_MODES.md).
- **Image Workflow API**:
  - `GET /api/image-workflow/status`
  - `POST /api/image-workflow/upload` (multipart `boot` + `files`)
  - `POST /api/image-workflow/validate`
  - `POST /api/image-workflow/generate`
  - `POST /api/marketing/social-research-stub` (deterministic positive verdict when lifestyle assets exist)
  - `POST /api/nuorder/mock-order` (demo confirmation; requires social stub first)
- **NuORDER (manufacturing handoff):** [Official API docs](https://assets.nuorder.com/api-docs/index.html) — OAuth 1.0 (`/api/initiate`, `/api/token`), then order APIs (e.g. **`PUT /api/order/new`** to create). The Next.js homepage shows a **stub preview** only (`frontend/lib/nuorderStub.js`); do not put consumer secrets in the browser — use a server-side proxy when you wire it up.

## What It Does
1. You create a design brief (foot dimensions, target user, materials)
2. OpenSCAD generates a parametric DXF cut file ready for CNC/laser cutting
3. A bill of materials (BOM) with per-pair costs is calculated automatically
4. FOY Project image workflow runs inside the same container for:
   - standardized prototype photo validation
   - AI lifestyle generation setup
   - segmentation/BOM workspace persistence under `/data/foy-project`

## Data Persistence
All data (SQLite database, CAD files, BOMs) is stored in /data inside the container. Mount `-v foy-data:/data` or `-v ./foy-data:/data` to persist across restarts.

## Architecture
```
Next.js UI (:3000) → FastAPI Backend (:8000) → SQLite DB + OpenSCAD
                                              → Local File Storage
```

## Operating modes (web vs terminal)

Customers can use the FOY stack through **the Next.js app** (browser chat + workflow, target design) and/or **Hermes in a terminal** (`docker exec -it`, same `/data` workspace). Optional **`hermes gateway`** covers external messengers (Slack/Telegram, etc.) when enabled.

See **[`FOY_OPERATING_MODES.md`](./FOY_OPERATING_MODES.md)** for the full split, shared paths, and suggested env flags (`FOY_CHAT_MODE`, `HERMES_GATEWAY_ENABLED`).

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `NEXT_PUBLIC_API_URL` | FastAPI URL for frontend | http://localhost:8000 |
| `STORAGE_PATH` | File storage directory | /data/foy-files |
| `DB_PATH` | SQLite database path | /data/database/foy.db |
| `FORCE_DB_INIT` | Reinitialize database on start | 0 (false) |
| `FOY_PROJECT_DIR` | FOY image workflow project directory | /data/foy-project |
| `HERMES_HOME` | Hermes Agent data directory (volume) | /data/hermes |
| `HERMES_BIN` | Hermes CLI path | /opt/hermes-venv/bin/hermes |
| `HERMES_GATEWAY_ENABLED` | Start `hermes gateway` under supervisord (`true` / `false`) | true |
| `OPENROUTER_API_KEY` | API key for **live** Hermes web chat (OpenRouter-compatible endpoint) | (unset → stub unless `HERMES_LLM_API_KEY`) |
| `OPENROUTER_BASE_URL` | OpenRouter-compatible API base | https://openrouter.ai/api/v1 |
| `OPENROUTER_MODEL` / `HERMES_WEB_CHAT_MODEL` | Model id for web chat (Hermes script prefers `HERMES_WEB_CHAT_MODEL` if set) | openai/gpt-4o-mini |
| `HERMES_LLM_PROVIDER` | Optional Hermes `AIAgent` provider id (e.g. `openrouter`) | (unset → Hermes default) |
| `HERMES_LLM_API_KEY` | Same role as `OPENROUTER_API_KEY` (e.g. Daytona `.env`); stdin/env resolution matches your other Hermes deploys | (unset) |
| `HERMES_LLM_BASE_URL` | Same role as `OPENROUTER_BASE_URL` | https://openrouter.ai/api/v1 |
| `HERMES_LLM_MODEL` | Same role as `OPENROUTER_MODEL` (after `HERMES_WEB_CHAT_MODEL`) | openai/gpt-4o-mini |
| `HERMES_WEB_MAX_ITERATIONS` | Max tool/LLM iterations per chat turn (Hermes `AIAgent`) | 8 |
| `HERMES_WEB_TURN_TIMEOUT` | Seconds allowed per `hermes_web_turn.py` subprocess | 300 |
| `HERMES_WEB_ENABLED_TOOLSETS` | Comma-separated Hermes toolsets for web chat (see [Toolsets Reference](https://hermes-agent.nousresearch.com/docs/reference/toolsets-reference)) | `safe,skills,terminal,file` |

## Hermes Agent (Phase 0–1)

Hermes is installed at build time into **`/opt/hermes-venv`** (isolated from the FastAPI app’s Python packages).  
Persistent config and state live under **`HERMES_HOME`** (default **`/data/hermes`** on the mounted volume).

The container runs **`hermes gateway`** under supervisord. Build pin:

```dockerfile
ARG HERMES_AGENT_REF=v2026.4.30
```

Override when building:

```bash
docker build --build-arg HERMES_AGENT_REF=v2026.4.30 -t foy-system:test .
```

Upstream install reference: [Hermes Agent installation](https://hermes-agent.nousresearch.com/docs/getting-started/installation)

## Building Yourself

**Hermes web chat needs an API key inside the container.** A `foy-docker/.env` on your machine is **not** read by Docker unless you pass it in.

- **Compose (recommended):** `cd foy-docker` then `docker compose up --build` — loads optional `./.env` (`OPENROUTER_API_KEY` or `HERMES_LLM_API_KEY`, etc.).
- **Host port already in use** (e.g. `Bind for 0.0.0.0:3000 failed: port is already allocated`): stop whatever holds **3000** / **8000**, or map different host ports, e.g. `FOY_HOST_PORT=3001 docker compose up --build` (UI at `http://localhost:3001`; API stays on host **8000**). If **8000** is busy too: `FOY_HOST_PORT=3001 FOY_API_HOST_PORT=8001 docker compose up --build` — you must then point the browser at the API (rebuild the image with `NEXT_PUBLIC_API_URL` matching that host port, or use a reverse proxy); the default UI build calls `http://localhost:8000`.
- **Next.js UI returns 500** on port **3000**: (1) Confirm traffic hits the container: `curl -i http://127.0.0.1:3000/healthz` should return **`200`** and body **`ok`**. If curl is **200** but the browser shows **500**, try **`127.0.0.1`** instead of **`localhost`**, another browser, or disable extensions (some proxies break RSC). (2) **`HOSTNAME`**: Next standalone uses it for `listen()`; this image forces **`0.0.0.0`** in the Dockerfile, supervisord, and **after** sourcing **`/data/hermes/.env`** in `entrypoint.sh` so a volume `.env` cannot override it. Rebuild after pulling changes.
- **`docker run`:** pass your file explicitly, e.g. `docker run --rm --env-file .env -p 3000:3000 -p 8000:8000 …`
- **Volume file:** after first run, you can add **`/data/hermes/.env`** on the data volume; the entrypoint sources it so uvicorn and `hermes_web_turn.py` see the same vars.

```bash
cd foy-docker
docker build -t ghcr.io/deboboy/foy-system:latest .
docker run --rm -e HOSTNAME=0.0.0.0 -p 3000:3000 -p 8000:8000 ghcr.io/deboboy/foy-system:latest
# ↑ add --env-file .env for LLM keys; HOSTNAME avoids Next standalone bind issues in Docker
```

## Tech Stack
- Frontend: Next.js 14 (React 18, standalone output) — see `frontend/package.json`
- Backend: FastAPI (Python 3.12)
- Database: SQLite (WAL mode)
- CAD Engine: OpenSCAD (parametric DXF generation)
- Process Manager: supervisord (FastAPI + Next.js + optional Hermes gateway)

## Limitations
- Single user, no authentication (demo mode)
- SQLite instead of PostgreSQL (fine for single-user)
- OpenRouter not required (no AI dependency - CAD is purely parametric)

## License
Internal use - Last Myle Engineering
