[object Object]

← back to Security Dashboard

prod-ready: env PORT + PROBE_LOCAL on-box probe mode, /api/health, relative api/status fetch (subpath-safe), ecosystem.config.cjs (:9769), .deploy.conf

3b1fd390596e144d52f9488b6848356e935df5e2 · 2026-05-31 19:59:52 -0700 · SteveStudio2

Files touched

Diff

commit 3b1fd390596e144d52f9488b6848356e935df5e2
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Sun May 31 19:59:52 2026 -0700

    prod-ready: env PORT + PROBE_LOCAL on-box probe mode, /api/health, relative api/status fetch (subpath-safe), ecosystem.config.cjs (:9769), .deploy.conf
---
 .deploy.conf         |  5 +++++
 ecosystem.config.cjs | 12 ++++++++++++
 public/index.html    |  2 +-
 server.js            | 20 +++++++++++++++++---
 4 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/.deploy.conf b/.deploy.conf
new file mode 100644
index 0000000..05b905f
--- /dev/null
+++ b/.deploy.conf
@@ -0,0 +1,5 @@
+PROJECT_NAME="security-dashboard"
+DEPLOY_PATH="/root/public-projects/security-dashboard"
+HEALTH_URL="http://127.0.0.1:9769/api/health"
+# zero-dependency (no package.json) — skip npm install on remote
+INSTALL_CMD="true"
diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs
new file mode 100644
index 0000000..ffd803b
--- /dev/null
+++ b/ecosystem.config.cjs
@@ -0,0 +1,12 @@
+// prod (Kamatera) launch — PROBE_LOCAL makes the probe run on-box instead of
+// ssh-ing to itself; served behind admin.agentabrams.com/security/ (IP-locked).
+module.exports = {
+  apps: [{
+    name: 'security-dashboard',
+    script: 'server.js',
+    cwd: '/root/public-projects/security-dashboard',
+    env: { PORT: '9769', PROBE_LOCAL: '1' },
+    autorestart: true,
+    max_memory_restart: '150M',
+  }],
+};
diff --git a/public/index.html b/public/index.html
index 555035b..e06deff 100644
--- a/public/index.html
+++ b/public/index.html
@@ -225,7 +225,7 @@ function render(d){
 
 async function load(force){
   try{
-    const r=await fetch('/api/status'+(force?'?t='+Date.now():''));
+    const r=await fetch('api/status'+(force?'?t='+Date.now():''));
     render(await r.json());
   }catch(e){ document.getElementById('checked').innerHTML='<span style="color:var(--red)">probe failed</span>'; }
 }
diff --git a/server.js b/server.js
index 85c36c7..5f5893d 100644
--- a/server.js
+++ b/server.js
@@ -7,7 +7,7 @@ const fs = require('fs');
 const path = require('path');
 const { execFile } = require('child_process');
 
-const PORT = 9768;
+const PORT = parseInt(process.env.PORT || '9768', 10);
 const PUBLIC = path.join(__dirname, 'public');
 const REGISTRY = `${process.env.HOME}/Projects/secrets-manager/registry.json`;
 
@@ -37,9 +37,18 @@ echo "UFW_DEFAULT=$UFW_DEFAULT"
 echo "UID0_BACKDOOR=\${PAKCHOI:-none}"
 `;
 
+// On Mac (dev) we reach prod via the `ssh my-server` alias. When this app is
+// itself hosted ON prod (PROBE_LOCAL=1), localhost IS prod — run the probe
+// script directly with `bash -s` instead of ssh-ing to ourselves.
+const PROBE_LOCAL = process.env.PROBE_LOCAL === '1';
+
 function probeProd() {
   return new Promise((resolve) => {
-    execFile('ssh', ['my-server', 'bash -s'], { timeout: 25_000 }, (err, stdout) => {
+    const child = PROBE_LOCAL
+      ? execFile('bash', ['-s'], { timeout: 25_000 }, onDone)
+      : execFile('ssh', ['my-server', 'bash -s'], { timeout: 25_000 }, onDone);
+    child.stdin.end(PROBE);
+    function onDone(err, stdout) {
       const kv = {};
       (stdout || '').split('\n').forEach((l) => {
         const i = l.indexOf('=');
@@ -47,7 +56,7 @@ function probeProd() {
       });
       kv._error = err ? String(err.message || err).slice(0, 120) : null;
       resolve(kv);
-    }).stdin.end(PROBE);
+    }
   });
 }
 
@@ -90,6 +99,11 @@ async function status() {
 }
 
 const server = http.createServer(async (req, res) => {
+  if (req.url === '/api/health') {
+    res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
+    res.end(JSON.stringify({ status: 'ok', probe: PROBE_LOCAL ? 'local' : 'ssh' }));
+    return;
+  }
   if (req.url === '/api/status') {
     try {
       const data = await status();

← cf95d58 dashboard: mark 3 leaks env-ified (code closed; still rotate  ·  back to Security Dashboard  ·  gitignore: add missing standard excludes (dist/ build/ .next 30cc8fb →