2:I[7012,["4765","static/chunks/4765-f5afdf8061f456f3.js","9856","static/chunks/9856-3b185291364d9bef.js","6687","static/chunks/app/docs/%5B...slug%5D/page-e07536548216bee4.js"],"MarkdownRenderer"] 4:I[9856,["4765","static/chunks/4765-f5afdf8061f456f3.js","9856","static/chunks/9856-3b185291364d9bef.js","6687","static/chunks/app/docs/%5B...slug%5D/page-e07536548216bee4.js"],""] 5:I[4126,[],""] 7:I[9630,[],""] 8:I[4278,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"HeadingProvider"] 9:I[1476,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"Header"] a:I[3167,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"Sidebar"] b:I[7409,["9856","static/chunks/9856-3b185291364d9bef.js","8172","static/chunks/8172-b3a2d6fe4ae10d40.js","3185","static/chunks/app/layout-2814fa5d15b84fe4.js"],"PageFrame"] 3:T2478, # Docs Site Deployment and TLS Runbook **Last Updated:** 2025-11-27 **URL:** https://assistdocs.asimo.io **Document Root:** `/var/www/assistdocs.asimo.io` --- ## Quick Deployment Checklist ```bash # 1. Navigate to repo cd ~/VoiceAssist # 2. Pull latest changes git pull origin main # 3. Install dependencies (if needed) pnpm install # 4. Navigate to docs-site cd apps/docs-site # 5. Validate metadata and links pnpm validate:metadata pnpm check:links # 6. Generate agent JSON (if docs changed) pnpm generate-agent-json # 7. Build the static site pnpm build # 8. Deploy to Apache document root sudo rm -rf /var/www/assistdocs.asimo.io/* sudo cp -r out/* /var/www/assistdocs.asimo.io/ # 9. Verify deployment curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/ curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/agent/index.json curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/agent/docs.json curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/search-index.json ``` --- ## Architecture Overview ### Build Process ``` docs/*.md → Next.js static export apps/docs-site/ → Build artifacts in out/ scripts/generate-agent-json → public/agent/*.json → search-index.json ``` ### Deployment Architecture ``` ┌──────────────────────────────────────────────────┐ │ Apache2 (mod_ssl, mod_rewrite) │ │ - assistdocs.asimo.io-le-ssl.conf │ │ - DocumentRoot: /var/www/assistdocs.asimo.io │ │ - RewriteEngine for clean URLs │ └──────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────┐ │ Static Files │ │ - /*.html (Next.js pages) │ │ - /agent/*.json (AI agent endpoints) │ │ - /search-index.json (Fuse.js) │ │ - /sitemap.xml │ └──────────────────────────────────────────────────┘ ``` --- ## Step 1: Prepare for Deployment ### 1.1 Sync Repository ```bash cd ~/VoiceAssist git pull origin main git status # Verify clean state ``` ### 1.2 Install Dependencies ```bash # Root level (pnpm workspace) pnpm install # Verify docs-site dependencies cd apps/docs-site ls node_modules/.bin/next # Should exist ``` --- ## Step 2: Validate Documentation ### 2.1 Metadata Validation ```bash cd ~/VoiceAssist/apps/docs-site pnpm validate:metadata ``` **Expected Output:** No errors about missing or invalid frontmatter. ### 2.2 Link Validation ```bash pnpm check:links ``` **Expected Output:** All internal links resolve correctly. ### 2.3 Fix Common Issues **Missing frontmatter:** ```yaml --- title: "Document Title" slug: "path/to-document" summary: "Brief description" status: stable stability: production owner: team lastUpdated: "YYYY-MM-DD" audience: ["human", "agent"] tags: ["tag1", "tag2"] category: category-name --- ``` **Broken links:** Update markdown links to use relative paths from docs/ directory. --- ## Step 3: Generate Agent JSON The agent JSON files provide machine-readable access to documentation. ### 3.1 Run Generation Script ```bash cd ~/VoiceAssist/apps/docs-site pnpm generate-agent-json ``` ### 3.2 Verify Output ```bash # Check index.json cat public/agent/index.json | jq '.name' # Should output: "VoiceAssist Documentation" # Check docs.json count cat public/agent/docs.json | jq 'length' # Should output document count (e.g., 220+) # Check search index ls -la public/search-index.json ``` --- ## Step 4: Build Static Site ### 4.1 Run Build ```bash cd ~/VoiceAssist/apps/docs-site pnpm build ``` **Expected Output:** - `✓ Compiled successfully` - `Export successful` - Files in `out/` directory ### 4.2 Verify Build Output ```bash ls out/ # Should contain: index.html, ai/, docs/, agent/, search-index.json, sitemap.xml ls out/agent/ # Should contain: index.json, docs.json, schema.json ``` --- ## Step 5: Deploy to Apache ### 5.1 Clear Old Files ```bash sudo rm -rf /var/www/assistdocs.asimo.io/* ``` ### 5.2 Copy New Build ```bash sudo cp -r ~/VoiceAssist/apps/docs-site/out/* /var/www/assistdocs.asimo.io/ ``` ### 5.3 Set Permissions ```bash sudo chown -R www-data:www-data /var/www/assistdocs.asimo.io sudo chmod -R 755 /var/www/assistdocs.asimo.io ``` ### 5.4 Reload Apache (if config changed) ```bash sudo apache2ctl configtest sudo systemctl reload apache2 ``` --- ## Step 6: Verify Deployment ### 6.1 Check HTTP Status ```bash # Main page curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/ # AI agent endpoints curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/agent/index.json curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/agent/docs.json curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/search-index.json # Clean URLs (should return 200, not 404) curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/ai/onboarding curl -s -o /dev/null -w "%{http_code}" https://assistdocs.asimo.io/ai/status ``` **Expected:** All should return `200`. ### 6.2 Check Content ```bash # Verify agent JSON content curl -s https://assistdocs.asimo.io/agent/index.json | jq '.endpoints' # Verify sitemap curl -s https://assistdocs.asimo.io/sitemap.xml | head -20 ``` --- ## TLS Certificate Management ### Current Certificate Status ```bash sudo certbot certificates | grep -A 5 "assistdocs.asimo.io" ``` **Current Certificate:** - **Domain:** assistdocs.asimo.io - **Issuer:** Let's Encrypt - **Key Type:** ECDSA - **Certificate Path:** `/etc/letsencrypt/live/assistdocs.asimo.io/fullchain.pem` - **Private Key Path:** `/etc/letsencrypt/live/assistdocs.asimo.io/privkey.pem` - **Expiry:** 2026-02-19 (auto-renewed) ### Automatic Renewal Certbot automatically renews certificates via systemd timer. ```bash # Check timer status sudo systemctl status certbot.timer # View renewal schedule sudo systemctl list-timers | grep certbot # Test renewal (dry run) sudo certbot renew --dry-run ``` ### Manual Renewal (if needed) ```bash # Renew specific certificate sudo certbot renew --cert-name assistdocs.asimo.io # Force renewal sudo certbot renew --cert-name assistdocs.asimo.io --force-renewal # Reload Apache after renewal sudo systemctl reload apache2 ``` ### New Certificate (if domain changes) ```bash sudo certbot --apache -d assistdocs.asimo.io ``` --- ## Apache Configuration ### Configuration File **Location:** `/etc/apache2/sites-available/assistdocs.asimo.io-le-ssl.conf` ### Key Configuration ```apache ServerName assistdocs.asimo.io DocumentRoot /var/www/assistdocs.asimo.io Options Indexes FollowSymLinks AllowOverride All Require all granted DirectoryIndex index.html # Clean URLs for Next.js static export RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.html -f RewriteRule ^(.*)$ $1.html [L] # SSL (managed by Certbot) SSLEngine on SSLCertificateFile /etc/letsencrypt/live/assistdocs.asimo.io/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/assistdocs.asimo.io/privkey.pem ``` ### Test Configuration ```bash sudo apache2ctl configtest ``` ### Reload After Changes ```bash sudo systemctl reload apache2 ``` --- ## Troubleshooting ### 404 for Clean URLs **Symptom:** `/ai/onboarding` returns 404 but `/ai/onboarding.html` works. **Cause:** RewriteEngine rules not applied. **Fix:** 1. Ensure `mod_rewrite` is enabled: `sudo a2enmod rewrite` 2. Verify rules are inside `` block 3. Reload Apache: `sudo systemctl reload apache2` ### Build Fails **Symptom:** `pnpm build` fails with errors. **Checks:** ```bash # Check for TypeScript errors pnpm tsc --noEmit # Check for missing dependencies pnpm install # Clear cache rm -rf .next out pnpm build ``` ### Agent JSON Not Updated **Symptom:** `/agent/docs.json` shows old documents. **Fix:** ```bash # Regenerate agent JSON pnpm generate-agent-json # Rebuild and redeploy pnpm build sudo cp -r out/* /var/www/assistdocs.asimo.io/ ``` ### TLS Certificate Expired **Symptom:** Browser shows certificate error. **Fix:** ```bash # Check certificate status sudo certbot certificates # Force renewal sudo certbot renew --cert-name assistdocs.asimo.io --force-renewal # Reload Apache sudo systemctl reload apache2 ``` --- ## Related Documentation - [Debugging Docs Site](../../debugging/DEBUGGING_DOCS_SITE.md) - [Implementation Status](../../overview/IMPLEMENTATION_STATUS.md) - [Internal Docs System](../../INTERNAL_DOCS_SYSTEM.md) 6:["slug","operations/runbooks/DOCS_SITE_DEPLOYMENT_AND_TLS","c"] 0:["X7oMT3VrOffzp0qvbeOas",[[["",{"children":["docs",{"children":[["slug","operations/runbooks/DOCS_SITE_DEPLOYMENT_AND_TLS","c"],{"children":["__PAGE__?{\"slug\":[\"operations\",\"runbooks\",\"DOCS_SITE_DEPLOYMENT_AND_TLS\"]}",{}]}]}]},"$undefined","$undefined",true],["",{"children":["docs",{"children":[["slug","operations/runbooks/DOCS_SITE_DEPLOYMENT_AND_TLS","c"],{"children":["__PAGE__",{},[["$L1",["$","div",null,{"children":[["$","div",null,{"className":"mb-6 flex items-center justify-between gap-4","children":[["$","div",null,{"children":[["$","p",null,{"className":"text-sm text-gray-500 dark:text-gray-400","children":"Docs / Raw"}],["$","h1",null,{"className":"text-3xl font-bold text-gray-900 dark:text-white","children":"Docs Site Deployment and TLS Runbook"}],["$","p",null,{"className":"text-sm text-gray-600 dark:text-gray-400","children":["Sourced from"," ",["$","code",null,{"className":"font-mono text-xs","children":["docs/","operations/runbooks/DOCS_SITE_DEPLOYMENT_AND_TLS.md"]}]]}]]}],["$","a",null,{"href":"https://github.com/mohammednazmy/VoiceAssist/edit/main/docs/operations/runbooks/DOCS_SITE_DEPLOYMENT_AND_TLS.md","target":"_blank","rel":"noreferrer","className":"inline-flex items-center gap-2 rounded-md border border-gray-200 dark:border-gray-700 px-3 py-1.5 text-sm text-gray-700 dark:text-gray-200 hover:border-primary-500 dark:hover:border-primary-400 hover:text-primary-700 dark:hover:text-primary-300","children":"Edit on GitHub"}]]}],["$","div",null,{"className":"rounded-lg border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-6","children":["$","$L2",null,{"content":"$3"}]}],["$","div",null,{"className":"mt-6 flex flex-wrap gap-2 text-sm","children":[["$","$L4",null,{"href":"/reference/all-docs","className":"inline-flex items-center gap-1 rounded-md bg-gray-100 px-3 py-1 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700","children":"← All documentation"}],["$","$L4",null,{"href":"/","className":"inline-flex items-center gap-1 rounded-md bg-gray-100 px-3 py-1 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700","children":"Home"}]]}]]}],null],null],null]},[null,["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children","docs","children","$6","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[null,["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children","docs","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/7f586cdbbaa33ff7.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","className":"h-full","children":["$","body",null,{"className":"__className_f367f3 h-full bg-white dark:bg-gray-900","children":[["$","a",null,{"href":"#main-content","className":"skip-to-content","children":"Skip to main content"}],["$","$L8",null,{"children":[["$","$L9",null,{}],["$","$La",null,{}],["$","main",null,{"id":"main-content","className":"lg:pl-64","role":"main","aria-label":"Documentation content","children":["$","$Lb",null,{"children":["$","$L5",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]]}]]}]}]],null],null],["$Lc",null]]]] c:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"Docs Site Deployment and TLS Runbook | Docs | VoiceAssist Docs"}],["$","meta","3",{"name":"description","content":"Step-by-step guide for building, deploying, and managing TLS for assistdocs.asimo.io."}],["$","meta","4",{"name":"keywords","content":"VoiceAssist,documentation,medical AI,voice assistant,healthcare,HIPAA,API"}],["$","meta","5",{"name":"robots","content":"index, follow"}],["$","meta","6",{"name":"googlebot","content":"index, follow"}],["$","link","7",{"rel":"canonical","href":"https://assistdocs.asimo.io"}],["$","meta","8",{"property":"og:title","content":"VoiceAssist Documentation"}],["$","meta","9",{"property":"og:description","content":"Comprehensive documentation for VoiceAssist - Enterprise Medical AI Assistant"}],["$","meta","10",{"property":"og:url","content":"https://assistdocs.asimo.io"}],["$","meta","11",{"property":"og:site_name","content":"VoiceAssist Docs"}],["$","meta","12",{"property":"og:type","content":"website"}],["$","meta","13",{"name":"twitter:card","content":"summary"}],["$","meta","14",{"name":"twitter:title","content":"VoiceAssist Documentation"}],["$","meta","15",{"name":"twitter:description","content":"Comprehensive documentation for VoiceAssist - Enterprise Medical AI Assistant"}],["$","meta","16",{"name":"next-size-adjust"}]] 1:null