# TRIAD DISPLAYS — OPERATIONAL RUNBOOK
**Version:** 2.0 | **Last Updated:** July 16, 2026 | **Author:** Airo / GoDaddy Builder
**Live Site:** https://www.triaddisplays.com | **Preview:** https://icvwrkl6e1.c39.airoapp.ai

---

## TABLE OF CONTENTS
1. [System Overview](#1-system-overview)
2. [Architecture Summary](#2-architecture-summary)
3. [Tech Stack](#3-tech-stack)
4. [Pages & Routes](#4-pages--routes)
5. [API Endpoints](#5-api-endpoints)
6. [Security Architecture](#6-security-architecture)
7. [Admin Panel Guide](#7-admin-panel-guide)
8. [Ecosystem Brands Management](#8-ecosystem-brands-management)
9. [AI Chat & Knowledge Base](#9-ai-chat--knowledge-base)
10. [Food Images Library](#10-food-images-library)
11. [Data Persistence](#11-data-persistence)
12. [Secrets & Environment Variables](#12-secrets--environment-variables)
13. [Upgrade Guide](#13-upgrade-guide)
14. [Incident Response](#14-incident-response)
15. [DNS & Domain](#15-dns--domain)
16. [Backup & Recovery](#16-backup--recovery)
17. [Known Issues & Tech Debt](#17-known-issues--tech-debt)
18. [Changelog](#18-changelog)

---

## 1. SYSTEM OVERVIEW

Triad Displays is a full-stack React/TypeScript web application for a B2B point-of-purchase display company. The site serves as:

- **Marketing site** — Product pages, gallery, about, contact
- **E-commerce lead gen** — Quote requests, contact forms, specials
- **Food image library** — 1,413 verified food images across 24 categories
- **AI-powered support** — Gemini 2.5 Flash chatbot with product knowledge
- **Knowledge base** — 6 categories, 20+ searchable support articles
- **Admin panel** — Full content, image, user, and ecosystem management
- **Ecosystem hub** — Footer strip linking 7 sister brands

**Business Contact:**
- Phone: 866-963-7446
- Email: support@triaddisplays.com
- Admin Login: `/login` (password stored as bcrypt hash in `/private/admin-config.json`)

---

## 2. ARCHITECTURE SUMMARY

```
┌─────────────────────────────────────────────────────────┐
│                    BROWSER (React 19)                    │
│  Vite + React Router v7 + Tailwind CSS + shadcn/ui      │
└──────────────────────┬──────────────────────────────────┘
                       │ SSR + API
┌──────────────────────▼──────────────────────────────────┐
│              EXPRESS SERVER (Node.js)                    │
│  entry.ts → middleware → API routes → SSR render        │
│                                                          │
│  Middleware stack:                                       │
│  1. securityHeaders (CSP, HSTS, X-Frame-Options, etc.)  │
│  2. requireAdminAuth (all /api/admin/* except login)    │
│  3. rateLimiter (login endpoint — 5 attempts/15 min)    │
└──────────────────────┬──────────────────────────────────┘
                       │
┌──────────────────────▼──────────────────────────────────┐
│                 PERSISTENT STORAGE                       │
│  /private/admin-config.json  — bcrypt password hash     │
│  /private/sessions.json      — active session tokens    │
│  /private/users.json         — customer accounts        │
│  /private/ecosystem.json     — footer brand data        │
│  /private/pages.json         — CMS page content         │
│  /private/images.json        — image metadata           │
└─────────────────────────────────────────────────────────┘
```

---

## 3. TECH STACK

| Layer | Technology | Version |
|---|---|---|
| Frontend framework | React | 19 |
| Language | TypeScript | 5.x |
| Build tool | Vite | 5.x |
| Routing | React Router | v7 (data router) |
| Styling | Tailwind CSS | 3.x |
| UI components | shadcn/ui + Radix UI | Latest |
| Animation | Motion (Framer) | Latest |
| Server | Express | 4.x |
| SSR | renderToString + StaticRouterProvider | — |
| SEO | @dr.pogodin/react-helmet | Latest |
| Password hashing | bcryptjs (cost 12) | Latest |
| AI chat | Google Gemini 2.5 Flash | API |
| State management | React hooks + Context | — |
| Data fetching | @tanstack/react-query | Latest |
| Forms | react-hook-form + zod | Latest |
| Rich text | Lexical | Latest |
| Hosting | GoDaddy Airo Builder | — |

---

## 4. PAGES & ROUTES

### Public Pages
| Route | File | Description |
|---|---|---|
| `/` | `pages/index.tsx` | Homepage with hero, products, testimonials |
| `/about` | `pages/about.tsx` | Company story, team, values |
| `/contact` | `pages/contact.tsx` | Contact form + map |
| `/faq` | `pages/faq.tsx` | Frequently asked questions |
| `/gallery` | `pages/gallery.tsx` | Product photo gallery |
| `/blog` | `pages/blog.tsx` | News & articles |
| `/specials` | `pages/specials.tsx` | Current promotions |
| `/brochures` | `pages/brochures.tsx` | Downloadable brochures |
| `/design-services` | `pages/design-services.tsx` | Graphic design offerings |
| `/food-images` | `pages/food-images.tsx` | 1,413-image food library |
| `/knowledge-base` | `pages/knowledge-base.tsx` | Support knowledge base |
| `/ai-support` | `pages/ai-support.tsx` | Gemini AI chat support |
| `/payments` | `pages/payments.tsx` | Payment information |
| `/point-of-purchase` | `pages/point-of-purchase.tsx` | POP distributor info |
| `/privacy-policy` | `pages/privacy-policy.tsx` | Privacy policy |
| `/terms-of-use` | `pages/terms-of-use.tsx` | Terms of use |
| `/disclaimer` | `pages/disclaimer.tsx` | Legal disclaimer |
| `/docs` | `pages/docs.tsx` | **Runbook & docs download** |

### Product Pages
| Route | File | Description |
|---|---|---|
| `/digital-menu-boards` | `pages/digital-menu-boards.tsx` | Digital boards overview |
| `/magnetic-menu-boards` | `pages/magnetic-menu-boards.tsx` | Magnetic boards overview |
| `/drive-thru` | `pages/drive-thru.tsx` | Drive-thru systems overview |
| `/light-boxes` | `pages/light-boxes.tsx` | LED lightboxes overview |
| `/magnetic/designer` | `pages/magnetic/designer.tsx` | Designer style |
| `/magnetic/wood-trim` | `pages/magnetic/wood-trim.tsx` | Wood trim style |
| `/magnetic/stainless-steel` | `pages/magnetic/stainless-steel.tsx` | Stainless steel style |
| `/magnetic/custom` | `pages/magnetic/custom.tsx` | Custom style |
| `/magnetic/value` | `pages/magnetic/value.tsx` | Value style |
| `/magnetic/face-styles` | `pages/magnetic/face-styles.tsx` | Face styles guide |
| `/magnetic/accessories` | `pages/magnetic/accessories.tsx` | Accessories |
| `/drive-thru/premium` | `pages/drive-thru/premium.tsx` | Premium package |
| `/drive-thru/value` | `pages/drive-thru/value.tsx` | Value package |
| `/drive-thru/digital` | `pages/drive-thru/digital.tsx` | Digital drive-thru |
| `/drive-thru/communication` | `pages/drive-thru/communication.tsx` | Communication systems |
| `/drive-thru/accessories` | `pages/drive-thru/accessories.tsx` | Drive-thru accessories |
| `/light-boxes/led` | `pages/light-boxes/led.tsx` | LED lightboxes |
| `/light-boxes/fluorescent` | `pages/light-boxes/fluorescent.tsx` | Fluorescent lightboxes |

### Auth & Admin Pages
| Route | File | Description |
|---|---|---|
| `/login` | `pages/login.tsx` | Admin login |
| `/user-login` | `pages/user-login.tsx` | Customer login/register |
| `/settings` | `pages/settings.tsx` | Admin panel (9 tabs) |
| `/dashboard` | `pages/dashboard.tsx` | Admin dashboard |

---

## 5. API ENDPOINTS

### Public Endpoints
| Method | Route | Description |
|---|---|---|
| GET | `/api/health` | Health check |
| GET | `/api/ecosystem` | Footer brand list (visible only) |
| GET | `/api/images/lookup` | Food image lookup by ID |
| POST | `/api/chat` | Gemini AI chat |
| POST | `/api/users/login` | Customer login |
| POST | `/api/users/register` | Customer registration |

### Admin Endpoints (require Bearer token)
| Method | Route | Description |
|---|---|---|
| POST | `/api/admin/login` | Admin login → returns session token |
| GET | `/api/admin/check` | Validate session token |
| POST | `/api/admin/logout` | Destroy session |
| POST | `/api/admin/change-password` | Change admin password |
| GET | `/api/admin/ecosystem` | List all brands (incl. hidden) |
| POST | `/api/admin/ecosystem` | Add/update/delete/reorder brand |
| GET | `/api/admin/pages` | List CMS pages |
| POST | `/api/admin/pages` | Save CMS page content |
| GET | `/api/admin/images` | List managed images |
| POST | `/api/admin/images` | Upload image |
| DELETE | `/api/admin/images` | Delete image |
| GET | `/api/admin/images/serve` | Serve image file |
| GET | `/api/admin/users` | List all users |
| DELETE | `/api/admin/users` | Delete user |
| POST | `/api/ai-content` | AI content generation |

---

## 6. SECURITY ARCHITECTURE

### Authentication Flow
```
User enters password
       ↓
POST /api/admin/login
       ↓
Rate limit check (5 attempts / 15 min / IP)
       ↓
bcrypt.compareSync(input, storedHash)  [cost factor 12]
       ↓
createSession(ip) → randomBytes(32).toString('hex')  [64-char token]
       ↓
Token stored in /private/sessions.json (TTL: 8 hours)
       ↓
Response: { ok: true, token }
Cookie: admin_session (httpOnly, secure, sameSite=strict)
       ↓
All /api/admin/* requests validated via requireAdminAuth middleware
```

### Security Headers (all responses)
| Header | Value |
|---|---|
| X-Frame-Options | SAMEORIGIN |
| X-Content-Type-Options | nosniff |
| X-XSS-Protection | 1; mode=block |
| Referrer-Policy | strict-origin-when-cross-origin |
| Strict-Transport-Security | max-age=31536000; includeSubDomains |
| Content-Security-Policy | default-src 'self'; (see middleware/securityHeaders.ts) |
| Permissions-Policy | camera=(), microphone=(), geolocation=() |

### Rate Limiting
- **Max attempts:** 5 per IP per 15-minute window
- **Lockout duration:** 30 minutes
- **Logging:** Every failed attempt logs IP + timestamp to server console

### Password Storage
- Algorithm: bcrypt, cost factor 12
- Stored at: `/private/admin-config.json`
- Migration: Plain-text passwords auto-hashed on first server start via `initAdminPassword()`

### Session Management
- Token: 64-char hex (cryptographically random)
- TTL: 8 hours
- Storage: `/private/sessions.json`
- Invalidation: Logout destroys token server-side; password change destroys ALL sessions

---

## 7. ADMIN PANEL GUIDE

**URL:** `/settings` (requires login at `/login`)
**Default password:** `Triad2024!` ← **CHANGE THIS IMMEDIATELY IN PRODUCTION**

### Tab Overview
| Tab | Purpose |
|---|---|
| Business | Company name, address, phone, hours |
| Pages | CMS editor for homepage sections |
| Design | Colors, fonts, theme |
| SEO | Meta titles, descriptions, Open Graph |
| Social | Social media links |
| Logo | Upload/manage logo |
| Users | View/delete customer accounts & newsletter subscribers |
| Ecosystem | Add/edit/delete/toggle footer brand cards |
| Security | Change admin password |

### Changing the Admin Password
1. Go to `/settings` → Security tab
2. Enter current password: `Triad2024!`
3. Enter new password (minimum 12 characters)
4. Click Save — all existing sessions are invalidated immediately

---

## 8. ECOSYSTEM BRANDS MANAGEMENT

**Data file:** `/private/ecosystem.json`
**Public API:** `GET /api/ecosystem` (visible brands only, sorted by order)
**Admin API:** `GET/POST /api/admin/ecosystem`

### Current Brands (July 2026)
| Order | Name | URL | Status |
|---|---|---|---|
| 1 | PikciaOS | pikciaos.com | Live |
| 2 | iDigitalMenus | idigitalmenus.com | Live |
| 3 | Drivethru.systems | drivethru.systems | Live |
| 4 | PikciaAi | — | Coming Soon |
| 5 | PikciaPrint | pikciaprint.com | Live |
| 6 | JatoliAi | — | Coming Soon |
| 7 | SEORuns | seoruns.com | Live |

### Adding a New Brand via Admin
1. `/settings` → Ecosystem tab
2. Click **Add Brand**
3. Fill: Name, URL, Tagline, Category, Status
4. Click **Add Brand** — appears in footer immediately

### Updating a Coming Soon Brand to Live
1. `/settings` → Ecosystem tab
2. Click pencil icon on the brand
3. Update URL and change Status to **Live**
4. Click **Save Changes**

---

## 9. AI CHAT & KNOWLEDGE BASE

### AI Chat Widget
- **Component:** `src/components/AIChatWidget.tsx`
- **Page:** `src/pages/ai-support.tsx`
- **Model:** Google Gemini 2.5 Flash
- **API Key:** `GEMINI_API_KEY` (stored in Airo secrets)
- **Endpoint:** `POST /api/chat`
- **Features:** Product catalog awareness, image number recognition, upselling, multi-turn conversation

### Knowledge Base
- **Page:** `src/pages/knowledge-base.tsx`
- **Structure:** 6 categories, 20+ articles, client-side search
- **Categories:** Getting Started, Digital Menu Boards, Magnetic Menu Boards, Drive-Thru Systems, LED Lightboxes, Ordering & Shipping

### Updating Knowledge Base Articles
Currently hardcoded in `knowledge-base.tsx`. To add/edit articles:
1. Open `src/pages/knowledge-base.tsx`
2. Find the `ARTICLES` array
3. Add a new object to the appropriate category's `articles` array
4. Format: `{ title: '...', content: '...' }` — supports `**bold**` and `- bullet` markdown

---

## 10. FOOD IMAGES LIBRARY

- **Page:** `/food-images`
- **Total images:** 1,413 verified
- **Categories:** 24 (Asian, BBQ, Breakfast, Burgers, Chicken, Desserts, Kids, Pizza, Salads, Seafood, Sides, Tacos, and more)
- **Source:** Served directly from triaddisplays.com server
- **Catalog file:** `src/lib/image-catalog.ts`
- **Features:** Search, sidebar category counts, lightbox viewer, image ID badges, copy protection

### Adding New Images
1. Upload images to triaddisplays.com server
2. Add entries to `src/lib/image-catalog.ts`
3. Format: `{ id: number, category: string, url: string, title: string }`

---

## 11. DATA PERSISTENCE

All persistent data lives in `/private/` — survives restarts and deployments.

| File | Contents | Managed By |
|---|---|---|
| `/private/admin-config.json` | Admin bcrypt password hash | `adminStore.ts` |
| `/private/sessions.json` | Active session tokens + expiry | `sessionStore.ts` |
| `/private/users.json` | Customer accounts + newsletter | `userStore.ts` |
| `/private/ecosystem.json` | Footer brand cards | `ecosystemStore.ts` |
| `/private/pages.json` | CMS page content | `pageStore.ts` |
| `/private/images.json` | Uploaded image metadata | `imageStore.ts` |

**Note:** `/private/` is NOT web-accessible. Files are read/written server-side only.

---

## 12. SECRETS & ENVIRONMENT VARIABLES

Managed via GoDaddy Airo Builder → Settings → Secrets.

| Secret Name | Purpose | Required |
|---|---|---|
| `GEMINI_API_KEY` | Google Gemini AI chat | Yes |
| `ADMIN_PASSWORD` | Fallback default admin password | Optional (defaults to `Triad2024!`) |

**Never** prefix secrets with `VITE_` — all secrets are backend-only.

---

## 13. UPGRADE GUIDE

### Adding a New Product Page
1. Create `src/pages/[category]/[product-name].tsx`
2. Copy structure from an existing product page (e.g., `magnetic/designer.tsx`)
3. Add route to `src/routes.tsx`
4. Add to sitemap in `src/lib/seo-routes.ts` with `priority: 0.95`
5. Add navigation link in `src/layouts/parts/Header.tsx`

### Adding a New API Endpoint
1. Create handler file: `src/server/api/[path]/[METHOD].ts`
2. Export default `async function handler(req, res) { ... }`
3. Import in `src/server/entry.ts` inside `// <api-imports>` block
4. Register: `app.[method]('/api/[path]', handler)` inside `// <api-registrations>` block
5. If admin-protected, the central middleware handles it automatically for `/api/admin/*`

### Updating the AI Chat System Prompt
1. Open `src/server/api/chat/POST.ts`
2. Find the `SYSTEM_PROMPT` constant
3. Edit the prompt text
4. Save — takes effect immediately (no restart needed)

### Adding a New Ecosystem Brand
**Via Admin (recommended):** `/settings` → Ecosystem tab → Add Brand

**Via code (for defaults):**
1. Open `src/server/ecosystemStore.ts`
2. Add entry to `DEFAULT_BRANDS` array
3. Run the node patch script or restart server to apply

### Upgrading Dependencies
```bash
# Check outdated packages
npm outdated

# Update all minor/patch versions
npm update

# Update a specific package
npm install [package]@latest

# After updating, always test:
npm run build
npm run type-check
```

### Deploying Updates
1. Make changes in Airo Builder
2. Click **Publish** button
3. Wait 2–5 minutes for build + deploy
4. Verify at https://www.triaddisplays.com

### Rotating the Admin Password
1. Log in to `/settings` → Security tab
2. Enter current password
3. Enter new password (12+ characters, mix of upper/lower/numbers/symbols)
4. Save — all sessions invalidated, re-login required

### Rotating the Gemini API Key
1. Go to Google AI Studio → API Keys
2. Create new key, copy it
3. In Airo Builder → Settings → Secrets
4. Update `GEMINI_API_KEY` with new value
5. Delete old key from Google AI Studio

---

## 14. INCIDENT RESPONSE

### Suspected Unauthorized Access
1. **Immediately:** Go to `/settings` → Security → Change password
   - This destroys ALL active sessions instantly
2. **Check logs:** Server logs show IP + timestamp of every login attempt
3. **Review:** Check `/private/sessions.json` for unexpected active sessions
4. **Rotate:** Change `GEMINI_API_KEY` if AI chat was abused
5. **Audit:** Review `/private/users.json` for unexpected accounts

### Site Down / Not Responding
1. Check GoDaddy Airo Builder status
2. Visit preview URL: `https://icvwrkl6e1.c39.airoapp.ai`
3. If preview works but domain doesn't → DNS issue (see Section 15)
4. If preview also down → Contact GoDaddy support

### AI Chat Not Working
1. Verify `GEMINI_API_KEY` is set in Airo Secrets
2. Check Google AI Studio for API quota/billing issues
3. Test endpoint: `POST /api/chat` with `{"message":"hello","history":[]}`

### Email Spoofing / Masquerading Attack
1. Add SPF record to DNS: `v=spf1 include:_spf.google.com ~all`
2. Add DMARC record: `v=DMARC1; p=quarantine; rua=mailto:dmarc@triaddisplays.com`
3. Enable DKIM in your email provider
4. These prevent anyone from sending email "from" @triaddisplays.com

---

## 15. DNS & DOMAIN

**Domain:** triaddisplays.com (registered at GoDaddy)
**Connected:** Yes — provisioning active as of July 16, 2026

### DNS Propagation
- After connecting domain: allow 2–48 hours for full propagation
- Test propagation: https://dnschecker.org/#A/triaddisplays.com
- Force local refresh: `ipconfig /flushdns` (Windows) or `sudo dscacheutil -flushcache` (Mac)

### Recommended DNS Records (add via GoDaddy DNS Manager)
| Type | Name | Value | Purpose |
|---|---|---|---|
| TXT | @ | `v=spf1 include:_spf.google.com ~all` | Prevent email spoofing |
| TXT | _dmarc | `v=DMARC1; p=quarantine; rua=mailto:admin@triaddisplays.com` | DMARC policy |
| TXT | @ | Google DKIM key (from your email provider) | Email authentication |

---

## 16. BACKUP & RECOVERY

### What to Back Up
All critical data is in `/private/`:
```
/private/admin-config.json   ← admin password hash
/private/sessions.json       ← active sessions (can be regenerated)
/private/users.json          ← customer accounts
/private/ecosystem.json      ← footer brands
/private/pages.json          ← CMS content
/private/images.json         ← image metadata
```

### Manual Backup via Admin
1. Go to `/settings` → Content tab
2. Use the export function to download page content

### Recovery
If `/private/` data is lost:
- `admin-config.json` — recreated on next server start using `ADMIN_PASSWORD` secret
- `ecosystem.json` — recreated from `DEFAULT_BRANDS` in `ecosystemStore.ts`
- `users.json` / `pages.json` — must be restored from backup

---

## 17. KNOWN ISSUES & TECH DEBT

| Issue | Severity | Notes |
|---|---|---|
| content-rules-lint warnings | Low | Inline strings in settings.tsx, food-images.tsx, knowledge-base.tsx, product pages not routed through virtual:content |
| User passwords plain text | Medium | `userStore.ts` stores customer passwords as plain text — should migrate to bcrypt |
| No email verification | Medium | Customer registration has no email confirmation step |
| Point of Purchase API | Low | Distributor API integration pending |
| Knowledge base hardcoded | Low | Articles are in TSX file, not a CMS — consider moving to content layer |

---

## 18. CHANGELOG

### v2.0 — July 16, 2026
- **SECURITY:** Replaced static session token with 64-char cryptographic tokens
- **SECURITY:** Migrated admin password to bcrypt hash (cost 12)
- **SECURITY:** Added brute-force rate limiting (5 attempts / 30-min lockout)
- **SECURITY:** Hardened cookie flags (httpOnly, secure, sameSite=strict)
- **SECURITY:** Added central admin auth middleware for all /api/admin/* routes
- **SECURITY:** Added security headers (CSP, HSTS, X-Frame-Options, Referrer-Policy)
- **SECURITY:** Added IP logging for all login attempts
- **FEATURE:** Added "Our Ecosystem" footer strip with 7 sister brands
- **FEATURE:** Added Ecosystem tab in Admin Settings (add/edit/delete/toggle brands)
- **FEATURE:** Added Drivethru.systems to ecosystem (LED & digital drive-thru solutions)
- **FEATURE:** Ecosystem data persisted in /private/ecosystem.json

### v1.0 — July 2026 (Initial Build)
- Full site rebuild with gradient modern redesign
- Gemini AI chat widget (Gemini 2.5 Flash)
- Food Images page (1,413 images, 24 categories)
- Knowledge Base (6 categories, 20+ articles)
- AI Support page with upsell sidebar
- All 5 product category pages with carousels
- Admin panel with 9-tab settings
- Real authentication with bcrypt + session management
- User management system
- SEO: Open Graph, JSON-LD schema, sitemap on all pages
- FAQPage schema on all product pages

---

*This runbook is auto-generated and maintained by Airo Builder.*
*For support: https://www.godaddy.com/help*
