# Do not wish to use heartcortex.ai/monitor? Run your own HEART Hub server

`https://heartcortex.ai/monitor` is free, but you do not have to use it. HEART Hub is open code (Apache-2.0), and **any site that follows the [HEART Hub profile](../spec/HEART-Monitor-1.0.md) works with every HEART device**. Run your own when you want your devices' health to stay on your own machine or network: for a company, a hospital, a farm, a home lab, or just because.

**Code:** [github.com/syncaissa/heartcortex.ai/tree/main/ownMonitorServer](https://github.com/syncaissa/heartcortex.ai/tree/main/ownMonitorServer), together with [`heart-monitor/`](https://github.com/syncaissa/heartcortex.ai/tree/main/heart-monitor), the site's own code.

| File | What it is |
|---|---|
| `server.mjs` | Your server: the HEART Hub site on Node, with its data in one file |
| `test_e2e.py` | Proves it works: sign in, register a simulated device, see its alerts, restart, nothing lost |
| `README.md` | This guide |
| `../heart-monitor/worker/worker.js` | The site itself, the same file heartcortex.ai runs (built from `heart-monitor/src/worker.src.js`) |

There are two ways to run your own:

| | **A. Your own computer or server** (this folder) | **B. Cloudflare Workers, like heartcortex.ai** |
|---|---|---|
| You need | A machine with Node 20+ | A Cloudflare account, an AWS account (DynamoDB) and an email sender (Resend) |
| Data lives | In one file, `data/monitor.json`, on your machine | In Amazon DynamoDB |
| Good for | One person, a team, a site on a private network | Many users, on the internet, with no server to look after |
| Steps | Below | [`heart-monitor/README.md`, "Deploying"](../heart-monitor/README.md) |

---

## A. Run it on your own machine, step by step

### Step 1: Get the code

You need the two folders `ownMonitorServer/` and `heart-monitor/` side by side, as they are in the repository:

```sh
git clone https://github.com/syncaissa/heartcortex.ai.git
cd heartcortex.ai
```

You need **Node 20 or newer** (`node --version`). Nothing else is installed: no packages, no database.

### Step 2: Start it

```sh
node ownMonitorServer/server.mjs
```

```
HEART Hub on http://127.0.0.1:8787  (listening on 127.0.0.1:8787, data in .../ownMonitorServer/data, email: console)
Point devices at it: HEART_MONITOR_URL=http://127.0.0.1:8787 (Python templates), CONFIG_HEART_MONITOR_URL (ESP32), --monitor-url (simulators)
```

Open `http://127.0.0.1:8787/monitor` in a browser. It is the same site as heartcortex.ai/monitor, on your machine.

At first start it makes `ownMonitorServer/data/` with two files, both readable only by you:

| File | What it holds |
|---|---|
| `secrets.json` | The session secret (signs sign-in cookies) and the approval signing key (signs owners' approval decisions; devices pin its public key). **Keep it.** Losing it signs everyone out, and devices that take approvals must be set up again. |
| `monitor.json` | Accounts, devices, their latest health and 90 days of alerts. Expired codes and sessions are dropped as it goes. |

### Step 3: Sign in

1. Open `/login` and enter your email address.
2. **Where is the code?** With the default `--email console`, the server prints the email it would have sent on its own console:

   ```
   --- email to you@example.org: 481022 is your HEART Hub code
   ...
   ---
   ```

   Type that code in. This is right for a server only you (or your team, at the console) use.
3. To send real emails instead, get a [Resend](https://resend.com) key for a domain you own, and start with:

   ```sh
   RESEND_API_KEY=re_... RESEND_FROM="HEART Hub <monitor@example.com>" node ownMonitorServer/server.mjs --email resend
   ```

### Step 4: Connect a device and register it

Every HEART device and simulator can report to any monitor site:

| Device | Point it at your server |
|---|---|
| Python template (`heart-templates/templates/python`) | `HEART_MONITOR_URL=http://127.0.0.1:8787 python3 heart.py` |
| ESP32 Full starter | `idf.py menuconfig` → HEART → monitor URL |
| A demo simulator (`pacemakerSimulator/`, ...) | `python3 pacemakerSimulator/pacemakerSimulator.py --monitor-url http://127.0.0.1:8787` |

The device prints a **device ID** and a **claim code**. On your server, open **Register a device**, enter them, and watch the device under **My devices**. Its page is private until you share it, and if it goes silent you get an email (on the console, or by Resend).

Try it in one minute:

```sh
node ownMonitorServer/server.mjs &
python3 pacemakerSimulator/pacemakerSimulator.py --monitor-url http://127.0.0.1:8787 --scenario all --speed 5
```

### Step 5: Let devices on your network reach it (HTTPS)

Devices refuse a monitor site without HTTPS, except on the same machine (`127.0.0.1`), because a report carries the device's key. So for devices on a network, put HTTPS in front of the server. The simplest is [Caddy](https://caddyserver.com), which gets and renews a certificate by itself:

```
# /etc/caddy/Caddyfile
monitor.example.com {
    reverse_proxy 127.0.0.1:8787
}
```

Then start the server with your address, and tell it to trust Caddy for the visitor's address (used by the sign-in limits):

```sh
node ownMonitorServer/server.mjs --site-url https://monitor.example.com --trust-proxy
```

Keep the server itself on `127.0.0.1` (the default), so only Caddy can reach it. On a private network without a public name, use a certificate from your own certificate authority, and install that authority on your devices.

### Step 6: Keep it running

A systemd service, so it starts at boot and restarts if it stops:

```ini
# /etc/systemd/system/heart-monitor.service
[Unit]
Description=HEART Hub
After=network.target

[Service]
User=heart
WorkingDirectory=/opt/heartcortex.ai
ExecStart=/usr/bin/node ownMonitorServer/server.mjs --site-url https://monitor.example.com --trust-proxy
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

```sh
sudo systemctl enable --now heart-monitor
journalctl -u heart-monitor -f        # with --email console, sign-in codes appear here
```

### Step 7: Back it up and update it

- **Back up** the `data/` folder (both files). To restore, stop the server, put the folder back, start it.
- **Update:** `git pull`, then restart the service. The data file stays as it is.
- The "gone quiet" check runs every 5 minutes inside the server; there is no cron to set up.

### Options

| Option | Default | Meaning |
|---|---|---|
| `--port` | 8787 | Port to listen on |
| `--host` | `127.0.0.1` | Address to listen on. Use `0.0.0.0` only behind your own HTTPS proxy |
| `--site-url` | `http://127.0.0.1:PORT` | The address people and devices use; must be `https://` except for `127.0.0.1` |
| `--data` | `ownMonitorServer/data` | Where the data file and secrets live |
| `--email` | `console` | `console` (print emails here) or `resend` (needs `RESEND_API_KEY` and `RESEND_FROM`) |
| `--trust-proxy` | off | Take the visitor's address from `X-Forwarded-For` (only behind your own proxy) |
| `--admin` | none | Administrator email addresses, comma-separated |

### Check that it works

```sh
python3 ownMonitorServer/test_e2e.py
```

It starts a fresh server, signs in with the code printed on its console, registers the pacemaker simulator with its claim code, sees its alert on the device page, checks the page is private, then restarts the server and checks that the account, the session and the device were kept, and that the device keeps reporting.

---

## What your server does, and does not, keep

Exactly what heartcortex.ai keeps, because it is the same code:

- **Only public data from devices:** the Pulse status and each alert's fault code, severity and times. Never a reading, a name or anything private: the site drops everything else, even if a device sends it.
- **Sign-in codes, claim codes, device keys and sessions only as hashes**, so a stolen data file cannot be used to sign in or to report.
- **The latest Pulse and 90 days of alerts.** An owner can delete a device and everything about it at once.
- **No commands to devices.** A monitor site can never send a device a command; it can only pass on an owner's signed yes or no to a change the device itself asked about.

## Limits of this server

- It is one process with its data in memory and one file. It suits up to a few hundred devices; for more, use option B.
- It has no "are you a person" check (Cloudflare Turnstile). The per-address and per-network sign-in limits still apply.
- The data file is written after every change; on a slow disk with many devices, prefer option B.

Licence: Apache-2.0. Part of HEART, the open standard at [heartcortex.ai](https://heartcortex.ai).
