# Docker Engine with a HEART: built in, or as a sidecar (experimental)

**To repeat the experiment step by step, read [REPLICATE.md](REPLICATE.md). Results: [results/SUMMARY.md](results/SUMMARY.md).**

**Status:** experimental. Built from Moby `docker-v29.9.3` (Docker Engine 29.9.3) with one new package, tested on a real daemon running real containers. Not a Docker release, and not endorsed by Docker. Licence: Apache-2.0, like Moby.

## What it adds

| | |
|---|---|
| **One new package, `daemon/heart`** | Standard library only, so no new dependencies. It serves the HEART Basic API on `127.0.0.1:43278` (or `7685`): the Card, the Pulse and alerts. |
| **One hook in `daemon/command/daemon.go`** | `heart.Start(ctx, hosts, cli.Config.Root)`, called right after the daemon tells systemd it is ready. |
| **How it reads Docker** | Only through the daemon's own Engine API socket (`/_ping`, `/info`, `/containers/json`, `/events`). It never touches the daemon's internals, environment variables, logs or files. |

## What it notices, with no network and no AI model

| What happens | Fault code |
|---|---|
| A container keeps restarting (3 starts in 5 minutes) | `system.main-function-degraded` |
| A container's health check fails | `system.main-function-degraded` |
| A container is killed for lack of memory | `performance.memory-exhausted` |
| The disk under the data root passes 90% | `storage.nearly-full` |
| The Engine API stops answering (3 checks) | `system.main-function-lost` |

## Privacy

- **Anyone** sees the Card, the Pulse status and the fault codes.
- **With the owner key** you also see container names, counts and disk figures. The key is made at first start, kept in `<data-root>/heart/owner.key` (mode 600), and printed once in the daemon's log.
- **Loopback only:** the HEART refuses any address that is not loopback, because it holds the power of the Docker socket.
- `HEART=off` turns it off.

## Files

| File | What it is |
|---|---|
| `heart/heart.go`, `statfs_*.go`, `heart_test.go` | The package, and its unit tests against a fake Engine |
| `heart/cmd/heart-docker-sidecar` | The same package as its own program, beside an unchanged `dockerd` (way B) |
| `run_both.sh`, `summarize.py` | Both ways, 3 runs each; and the summary table |
| `REPLICATE.md` | Every step, with the exact versions, commands and our results |
| `apply_patch.py` | Adds the package and the hook to a Moby source tree |
| `pod_main.sh` | Clone, patch, test, build, then the end-to-end tests, then the upload to B2. Runs on a RunPod CPU pod. |
| `test_heart_docker.py` | End-to-end tests against the real patched `dockerd` and Docker's own containerd and runc: real faults in real containers, and a comparison with the stock `dockerd` |
| `runpod_run.py` | Starts the pod, watches B2 for the result, and always terminates the pod |
| `b2.py` | Uploads to and downloads from B2 with the B2 native API |
| `results/` | Results of each run |
| `test_monitor_e2e.py` | End to end with HEART Hub: register, figures, Ask AI (a fake Claude API) and changing CPU and memory from the site |
| `screenshots/` | The live run on heartcortex.ai: registering, figures, Ask AI with Claude, and changing the CPU limit ([walkthrough](screenshots/README.md)) |

## On B2 (`experimentalDocker/`)

| Folder | What is in it |
|---|---|
| `source/` | The full modified Moby source |
| `bin/docker-v29.9.3/dockerd-heart` | The binary, with its SHA-256 |
| `patch/` | The change as a patch |
| `scripts/` | These files |
| `results/<run>/` | The results of each run |

## Run it yourself

```sh
python3 apply_patch.py path/to/moby
cd path/to/moby && go build -o dockerd-heart ./cmd/dockerd
sudo python3 test_heart_docker.py --heart ./dockerd-heart --bin /path/to/docker-static --out results/
```
