This system is designed to be adapted. Here’s how to make it yours.
The semaphore grid in dashboard/cto-cockpit.html is driven by the CONFIG.services array:
const CONFIG = {
services: [
{
id: 'n8n',
name: 'n8n',
pingUrl: 'https://YOUR-N8N.onrender.com/healthz',
description: 'Automation engine',
},
// Add more services here
],
};
Adding a service:
{
id: 'stripe', // unique ID, used to match status data
name: 'Stripe', // display name
pingUrl: null, // optional direct health check URL
description: 'Payments',
}
Status data comes from your statusEndpoint. That n8n webhook should return an object keyed by service ID:
{
"stripe": {
"status": "green",
"label": "Operational",
"detail": "Last payment: 2h ago",
"time": "5m ago"
}
}
You can build the status aggregator as a simple n8n workflow that:
Follow the WF-01 pattern:
Scheduled daily summary:
Schedule (9:00 AM) → Fetch Supabase stats → Format message → Send Slack notification
GitHub PR monitoring:
GitHub webhook → Filter PRs → Check age → Alert if open >48h → Supabase incident insert
Cloudflare analytics pull:
Schedule → Cloudflare API → Transform → Supabase upsert → Update status endpoint
Key n8n nodes to know:
HTTP Request — Call any REST APISupabase — Read/write your databaseFunction — Custom JavaScriptIF — Conditional branchingSchedule — Time-based triggersSlack — Send messages to channelsRespond to Webhook — Return JSON to the callerdashboard/cto-cockpit.html is a self-contained HTML file. No build tools.
Find the :root CSS block at the top:
:root {
--bg: #0a0a0a; /* Background */
--bg-card: #111111; /* Card background */
--green: #00ff88; /* Operational / accent */
--yellow: #ffcc00; /* Warning / degraded */
--red: #ff4444; /* Critical / down */
--blue: #6688ff; /* Info / secondary */
}
Change these values to match your brand. The rest of the CSS uses these variables.
<div class="section-header">Your Section</div> in the HTML<div id="your-section"></div>loadYourData() function that fetches and formats datarefresh() function alongside the othersrenderYourSection(data) function that builds the HTMLInclude Chart.js via CDN:
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
Then add a canvas in your section and initialize the chart in your render function. Chart.js works well with the dark theme — use backgroundColor: 'rgba(0, 255, 136, 0.1)' and borderColor: '#00ff88' to match.
The morningSync() function builds the summary text. Edit the logic to include your own metrics:
async function morningSync() {
// ... existing code ...
// Add your own summary line:
const metadataCount = await getMetadataCount(); // your Supabase query
summary += `${metadataCount} new entries this week.`;
}
CREATE TABLE statement following the pattern in schema/supabase-schema.sqlupdated_at triggerTemplate:
CREATE TABLE IF NOT EXISTS your_table (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- your columns
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Your policy name"
ON your_table FOR SELECT
USING (true); -- adjust conditions as needed
CREATE TRIGGER your_table_updated_at
BEFORE UPDATE ON your_table
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
https://YOUR-N8N.onrender.com/webhook/wf01-ingestapplication/jsonAdd a Schedule trigger in n8n and an HTTP Request node that calls the API’s endpoint. Transform the response in a Function node and send it to WF-01’s webhook or insert directly into Supabase.
Example — Cloudflare analytics:
// In a Function node after Cloudflare API call
const stats = $input.item.json.result.totals;
return {
json: {
text: `Cloudflare daily: ${stats.requests.all} requests, ${stats.threats.all} threats blocked`,
source: 'cloudflare',
user_id: 'system',
}
};
The narrative_role and timeline_group fields in WF-01 and WF-04 use a custom taxonomy. Change it to match your project:
In WF-01 (Claude API Classification node), edit the prompt:
"narrative_role": "your_role_1|your_role_2|your_role_3"
In WF-04 (Temporal Clustering node), change the date ranges:
if (year >= 2020 && year <= 2022) {
timeline_group = 'early-2020s';
} else if (year >= 2023 && year <= 2024) {
timeline_group = 'growth-phase';
} else if (year >= 2025) {
timeline_group = 'current';
}
Update groups in Narrative Arc Detection to match your new timeline_group values.
The Claude API Classification node in WF-01 is a standard HTTP Request. Replace it with any LLM API:
OpenAI (GPT-4):
https://api.openai.com/v1/chat/completions{ "model": "gpt-4o", "messages": [...], "max_tokens": 1024 }Authorization headerGoogle Gemini:
https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContentLocal LLM (Ollama):
http://localhost:11434/api/chatThe Parse Classification function node handles the response parsing — update the content extraction path to match your chosen API’s response format.
If you prefer not to use Render, run n8n locally or on any VPS:
docker run -d \
--name n8n \
-p 5678:5678 \
-e N8N_BASIC_AUTH_ACTIVE=true \
-e N8N_BASIC_AUTH_USER=admin \
-e N8N_BASIC_AUTH_PASSWORD=your-password \
-v n8n_data:/home/node/.n8n \
n8nio/n8n:latest
Import the workflow JSONs directly into n8n Cloud. The credentials and webhook URLs auto-configure.
If Supabase pausing or free tier limits become an issue, the SQL schema is standard PostgreSQL — runs on any Postgres host. Update the connection credentials in n8n.
Cloudflare Pages has identical free limits and faster global CDN. Copy the dashboard files to a Cloudflare Pages project. Update the deploy workflow in .github/workflows/deploy.yml to use Cloudflare’s GitHub Action.