Live status
Measured at 2026-10-27T06:10:37Z. When this page was last built, those figures were 3.9 days old. Every number below was read off the running estate by a command, at that moment, by a script that is in this repository. None was typed in, and none was copied from another page. The last column says what was counted, and where the label is wider than the count, it says so.
The other pages hold snapshots written by hand on a given day, and they drift: a hand-kept number is right once. This page exists so that there is one place where the figures are regenerated rather than remembered, and so that the staleness of those figures is itself a number you can read.
The referees, live
These three are not part of the snapshot. They are graded from outside and fetched by your browser when you open the page. The estate page says what each of them does and does not mean.
Notes on the badge wall
Moved here from the landing page on 2026-10-27, unchanged except where it is marked, because a reader with five minutes should meet the map before the measurement theory.
Three notes on that badge wall, because badges are where honesty usually goes to die.
The uptime numbers are not the goal. The cluster has been wiped and rebuilt, and patching reboots every node on a rolling basis. Powering nodes off mid-afternoon to see what happens was the practice earlier on; I have not verified that any node was powered off on purpose in the last 30 days. A scheduled fault injector also breaks things, but see the corrections: it works on a sandbox and cannot move this figure. This is a learning platform operated to production standards, not a product carrying an availability commitment.
The first badge says scheduled jobs, not alerting, because that is what it measures. It reports the worst status of every check in the account. A badge whose label is narrower than its scope will eventually be believed. It also read
lateonce while the alert pipeline was provably healthy. My first explanation (some other check was overdue) was wrong. The real cause was that the heartbeat check’s expected period equalled the sender’s interval, five minutes against five minutes, with no headroom, so a ping a second late was late by construction. Nothing was broken; the ruler was the same length as the thing measured. The monitoring was the least reliable component in the story, and a false positive gets the alarm muted, which manufactures the blindness of a false negative more slowly. That is why there is more than one referee: one can be wrong, and a single monitor gives you no way to find out.There is deliberately no long-window uptime figure. The provider will render 90, 180 and 365 days, and the series reads as a steadily improving record. It is not. On 2026-10-09 I read the monitor at 98.97% for 90 days, 99.485% for 180 and 99.746% for 365. Multiply each back out and every window holds the same 1,335 minutes of downtime: the monitor has less history than the window, so the numerator is fixed and only the denominator grows. The 7- and 30-day figures move independently, so those are measurements. The longer ones are arithmetic wearing a measurement’s clothes.
Where the 30-day shortfall comes from (added 2026-10-27). On 2026-10-09 the page said most of the downtime in the 30-day window was unattributed. It is no longer: I shut the whole estate down on purpose from 2026-10-14 to 2026-10-18, while I was away, and four and a half days is about 15 percent of thirty. While that gap is inside the window, the figure will read far below the seven-day one; it leaves the window in the middle of November. The thing worth saying about it is what the referees did and did not do. The off-site probe recorded it, as designed. The heartbeat badge recorded it. Neither can be told that a shutdown is planned, so both count it as downtime; a maintenance window is not built. Both notify by email only, which this event could not test, since I was the one who had switched it off (see the reliability page).
The cluster
What Kubernetes reports about itself. These are counts of API objects, so a CronJob that is suspended is still a CronJob, and a pod that finished is still a pod.
| Figure | Value | How it is measured |
|---|---|---|
| Nodes | 6 | kubectl get nodes; the number of node objects. |
| Nodes Ready | 6 | kubectl get nodes; nodes whose Ready condition is True. |
| Control-plane nodes | 3 | kubectl get nodes; nodes carrying the control-plane role label (each also runs etcd). |
| Agent nodes | 3 | kubectl get nodes; nodes without the control-plane role label. |
| CPU cores | 48 | kubectl get nodes; the sum of each node's reported CPU capacity. |
| Memory | 99.4 GB | kubectl get nodes; the sum of memory capacity in bytes, divided by 10^9. Decimal gigabytes, not GiB. |
| Pods running | 186 | kubectl get pods across all namespaces; pods in phase Running. |
| Pods, all phases | 216 | kubectl get pods across all namespaces; every pod object, including completed job pods. |
| Namespaces | 22 | kubectl get namespaces; every namespace object, empty and system ones included. |
| Containers in running pods | 228 | kubectl get pods; the sum of containers declared by pods in phase Running (init containers not counted). |
| Deployments | 70 | kubectl get deployments across all namespaces. |
| StatefulSets | 4 | kubectl get statefulsets across all namespaces. |
| DaemonSets | 8 | kubectl get daemonsets across all namespaces. |
| CronJobs | 10 | kubectl get cronjobs across all namespaces; suspended ones included. |
| Horizontal autoscalers | 5 | kubectl get horizontalpodautoscalers across all namespaces. |
| Ingresses | 12 | kubectl get ingresses across all namespaces. |
| Persistent volume claims | 49 | kubectl get persistentvolumeclaims across all namespaces. |
| Storage requested by claims | 914 GiB | kubectl get persistentvolumeclaims; the sum of requested size. Requested, not used. |
| Helm releases | 9 | kubectl get secrets filtered to Helm release type, labels only; distinct releases (one secret exists per revision). |
| Custom resource definitions | 117 | kubectl get customresourcedefinitions. |
| Age of the oldest node | 52 days | kubectl get nodes; whole days since the oldest node object was created. The cluster has been rebuilt from scratch, so this is not the age of the project. |
| Container restarts | 356 | kubectl get pods; the sum of restart counts over all containers and init containers that currently exist. |
Storage
Ceph, asked through its own toolbox. Raw is before replication; stored is what the applications actually wrote, so the ratio between them is the cost of replication.
| Figure | Value | How it is measured |
|---|---|---|
| Raw capacity | 800 GiB | ceph df, run through the toolbox pod; total raw bytes across all disks, before replication. |
| Raw capacity used | 555.6 GiB | ceph df; raw bytes in use, replicas included. |
| Data stored | 182.7 GiB | ceph df; the sum of the 'stored' figure of every pool, which is logical data before replication. |
| Disk daemons (OSDs) | 4 | ceph status; OSDs in the map. |
| OSDs up | 4 | ceph status; OSDs reporting up. |
| Monitors | 3 | ceph status; monitors in the monitor map. |
| Pools | 2 | ceph osd pool ls detail; every pool, the internal manager pool included. |
| Placement groups | 129 | ceph status; placement groups in total. |
| Placement groups active+clean | 129 | ceph status; placement groups in exactly the active+clean state. |
| Replica count | 3 | ceph osd pool ls detail; the lowest replica size of any pool, so one weak pool would show. |
| Ceph health | warn | ceph status; the overall health word (ok, warn or err). |
| Active health checks | 4 | ceph status; the number of health checks currently raised, each of which may cover several items. |
Databases
Postgres under an operator. The two synchronous rows ask different questions: one reads what the configuration requests, the other asks each primary what is true right now.
They agree at the moment of measurement.
| Figure | Value | How it is measured |
|---|---|---|
| Postgres clusters | 13 | kubectl get clusters of the Postgres operator; every cluster object. |
| Postgres instances | 34 | Same objects; the sum of reported instances (primaries and replicas). |
| Replicas | 21 | Same objects; instances minus one primary per cluster. |
| Clusters on PostgreSQL 16 | 5 | Reported major version of each cluster, confirmed by SELECT current_setting('server_version_num') on its primary. |
| Clusters on PostgreSQL 18 | 8 | Reported major version of each cluster, confirmed by SELECT current_setting('server_version_num') on its primary. |
| Clusters with a synchronous standby right now | 8 | Read-only SELECT on each primary: pg_stat_replication has a standby in sync or quorum state. What is true now, not what was configured. |
| Clusters configured for synchronous replication | 8 | kubectl get clusters of the Postgres operator; a synchronous setting or a minimum of synchronous replicas in the spec. What was asked for. |
| Backup objects | 348 | kubectl get backups of the Postgres operator; every backup object that still exists (older ones are pruned by retention). |
| Failed backups | 5 | Same objects; phase failed. |
| Backups completed in the last 24 hours | 13 | Same objects; phase completed with a stop time within 24 hours of the measurement. |
| Clusters archiving WAL | 13 | kubectl get clusters of the Postgres operator; ContinuousArchiving condition True. |
Observability
Prometheus, asked over its HTTP API. Read the history row before any rate. Retention is the lower of two limits, a time limit of 30 days and a size cap of 8 GiB, and the store holds 3.8 days at the moment of measurement. That is less than a week, so no weekly or monthly figure is published from it, and the failed-notifications row counts over the history that exists rather than over seven days.
| Figure | Value | How it is measured |
|---|---|---|
| Scrape targets up | 124 | Prometheus HTTP API through a temporary port-forward; count(up == 1). |
| Scrape targets | 124 | Prometheus HTTP API; count(up). |
| Head series | 258674 | Prometheus HTTP API; prometheus_tsdb_head_series, the series in the in-memory head block only, not every series in the store. It swings by a third over the head compaction cycle, so read it as an order of magnitude. |
| Samples ingested | 9192 per second | Prometheus HTTP API; rate of samples appended over the last five minutes. |
| Alert rules loaded | 227 | Prometheus HTTP API /rules; rules of type alerting, chart-supplied ones included. |
| Alert rules written here | 78 | kubectl get prometheusrules; alerting rules in objects not installed by a Helm chart. |
| Recording rules loaded | 86 | Prometheus HTTP API /rules; rules of type recording. |
| Dashboards provisioned | 31 | kubectl get configmaps carrying the dashboard label; JSON dashboards inside them, chart-supplied ones included. Dashboards made by hand in the UI are not counted. |
| Dashboards written here | 5 | Same objects as the row above, restricted to those not installed by a Helm chart (the same test as the alert rules written here); the remainder of the total is chart-supplied. |
| Metric retention, time limit | 30 days | Prometheus HTTP API /status/flags; the time-retention flag. Retention is the lower of the time limit and the size limit in the next row, so this is a ceiling, not the history actually held. |
| Metric retention, size limit | 8 GiB | Prometheus HTTP API /status/flags; the size-retention flag, read as binary units. 0 means no size limit. Whichever limit is reached first decides what is kept. |
| Metric history actually held | 3.8 days | Prometheus HTTP API; now minus the oldest sample time in the TSDB. Read it before trusting any weekly figure. |
| Failed alert notifications | 0 | Prometheus HTTP API; increase of the Alertmanager failure counter over the last 7 days, or over the history held if that is shorter (see the line above). |
| Alerts firing now | 5 | Prometheus HTTP API /alerts; alerts in state firing, including the two meta-alerts below. |
| Alerts firing now, excluding meta-alerts | 3 | Same; minus the heartbeat that is meant to fire permanently and the inhibitor that appears whenever an info-level alert is active. Both report on the alerting system, not the estate. |
Supply chain
What is running, and whether the signing story holds for the images that are ours. Ours means built and pushed to the estate’s own registry; a mirror of someone else’s software is counted as theirs, and if it is unsigned that is not a defect in our pipeline.
| Figure | Value | How it is measured |
|---|---|---|
| Distinct images running | 68 | kubectl get pods; distinct image references across containers and init containers of pods in phase Running. |
| First-party images | 7 | Same references; those pulled from the estate's own registry with a flat repository name. A mirror of someone else's software keeps its upstream path and is counted as third-party. |
| Third-party images | 61 | Distinct images running, minus first-party. |
| First-party images that verify | 6 | cosign verify against the release public key, by digest, for each running first-party image. |
| SBOMs accepted by the tracker | 71 | kubectl logs of the latest successful run of each SBOM job; uploads that returned HTTP 200. Acceptance, not analysis. |
CI
Parsed from the workflow files of the platform repository, not read from run history.
| Figure | Value | How it is measured |
|---|---|---|
| Workflow files | 39 | Listing the workflow directory of the platform repository's committed tree (git archive of HEAD). |
| Workflow jobs | 80 | Parsing each workflow file; entries under its jobs key. |
| Jobs on self-hosted runners | 77 | Parsing the runs-on line of each job; those naming self-hosted. |
| Jobs on GitHub-hosted runners | 0 | Parsing the runs-on line of each job; those naming an ubuntu, macos or windows image. Jobs that call a reusable workflow declare none. |
| Pre-commit hooks | 47 | Counting hook ids in the pre-commit configuration of the platform repository. |
The platform repository
Counted from the committed tree of the private platform repository, so unsaved work cannot move a number. The repository figures are the committed HEAD at generation time: 1943 commits, HEAD dated 2026-10-27. The test figures are static counts of test definitions in the source, not a pass rate: nothing is built or run to produce them.
| Figure | Value | How it is measured |
|---|---|---|
| Commits | 1943 | git rev-list --count HEAD in the platform repository. |
| Commits in the last 30 days | 257 | git rev-list --count --since='30 days ago' HEAD. |
| Repository age | 309 days | Whole days since the earliest root commit. |
| Decision records | 41 | Committed files; numbered Markdown files in the decision-record directory. |
| Open gaps | 87 | Parsing the gap register: rows of its ID tables. The register is a hand-kept document, so this is a parse, not a database query. |
| Closed gaps | 44 | Parsing the gap register: bold IDs on its Closed lines. |
| Make targets (summed per Makefile) | 413 | Committed Makefiles; target names read statically from each, without running make. Names are de-duplicated within one file and counted per file, so a name that exists in two Makefiles counts twice. The retired Docker Swarm tree is excluded. |
| Ansible playbooks | 59 | Committed files; top-level YAML files in a playbooks directory. The retired Docker Swarm tree is excluded. |
| Kubernetes manifest files | 82 | Committed files; YAML files under a manifests directory that declare a kind. |
| Runbooks | 9 | Committed files; Markdown files in a runbooks directory, indexes excluded. The retired Docker Swarm tree is excluded. |
| Documentation pages | 139 | Committed files; Markdown files under the docs directory. The retired Docker Swarm pages are excluded. |
| Lines of code, docs and config included | 149009 | cloc --git HEAD; code lines of the committed tree, excluding comments and blanks. Markdown, YAML, JSON and XML are counted as code by cloc, so this is the size of the tree, not of the programs. The retired Docker Swarm tree is excluded. |
| Lines of code, programming languages only | 79537 | The same cloc run, summing only programming languages (Dart, Java, Kotlin, Swift, Python, Go, C and C++, JavaScript, TypeScript, shell, SQL and similar); Markdown, YAML, JSON, XML, HTML, SVG and build or config formats are left out. |
| Platform repository HEAD at generation | 1943 commits, HEAD dated 2026-10-27 | git rev-list --count HEAD and the commit date of HEAD in the platform repository, taken at the moment of generation. Repository figures on this page are counted from that committed HEAD, not from a working tree. No commit hash is published. |
| Java test methods | 215 | Static count of @Test, @ParameterizedTest and @RepeatedTest annotations in committed test sources. Counted, not run. |
| Python test functions | 712 | Static count of test_ functions in committed application test files. Counted, not run; parametrised cases expand to more at run time. |
| Dart tests | 220 | Static count of test( and testWidgets( calls in committed Flutter test files. Counted, not run. |
| Loc8 app version | 1.6.1+2 | The version: line of the Loc8 app's committed pubspec.yaml (version+build number). The version the repository declares, not a store release: none of the three apps is in an app store. |
| Warn app version | 1.1.1+2 | The version: line of the Warn app's committed pubspec.yaml (version+build number). The version the repository declares, not a store release: none of the three apps is in an app store. |
| Talk app version | 0.3.1+4 | The version: line of the Talk app's committed pubspec.yaml (version+build number). The version the repository declares, not a store release: none of the three apps is in an app store. |
Hardware repositories
The sibling repositories, named here by role. Commits and static counts of test functions only; nothing is built or run to produce them. The C assertions of the rover platform are a separate row, because one test function holds several of them.
| Figure | Value | How it is measured |
|---|---|---|
| 3D printing repository, commits | 79 | git rev-list --count HEAD in the printing repository. |
| Embedded Linux (Raspberry Pi) repository, commits | 150 | git rev-list --count HEAD in the Yocto repository. |
| Rover platform repository, commits | 33 | git rev-list --count HEAD in the rover platform repository. |
| Rover platform tests | 190 | Static count of Python test_ functions and Go Test functions in committed test files. Counted, not run. C assertions are a separate row. |
| Rover platform C assertions | 36 | Static count of CHECK( assertions in committed C test files. An assertion is not a test: one test function holds several. Counted, not run. |
| Mower repository, commits | 246 | git rev-list --count HEAD in the mower repository. |
| Mower tests | 559 | Static count of Python test_ functions in committed test files. Counted, not run. |
| Video rover repository, commits | 3 | git rev-list --count HEAD in the video rover repository. |
| Video rover tests | 32 | Static count of Go Test functions in committed test files. Counted, not run. |
| i.MX8 repository, commits | 2 | git rev-list --count HEAD in the i.MX8 repository. |
The referees (snapshot)
The same public endpoints the badges above read, fetched by the generator at the time stamp, so each is the state at the generation instant. Unlike the badges, these do not move until the next run.
| Figure | Value | How it is measured |
|---|---|---|
| Public endpoint uptime, 7 days | 99.95% | The shields.io UptimeRobot JSON for the monitor behind the badge at the top of the estate page. |
| Public endpoint uptime, 30 days | 85.004% | The same monitor over 30 days. No longer window is published; see the estate page for why. |
| Scheduled-jobs heartbeat at generation | up | The healthchecks.io badge endpoint behind the badge at the top of the estate page; the state word it returned at the generation instant. The live badge above may differ by now. |
| GitHub stars on this repository | 1 | GitHub REST API, repository endpoint, unauthenticated. |
| Last push to this repository | 2026-10-22T08:55:59Z | GitHub REST API, repository endpoint; pushed_at. |
This site
This repository. The commit count is taken before the commit that carries the file.
| Figure | Value | How it is measured |
|---|---|---|
| Commits to this site | 119 | git rev-list --count HEAD in this repository, before the commit that carries this file. |
| Pages | 15 | Counting Markdown files in this site's docs directory. |
| Last Pages build | success | GitHub REST API, workflow runs; conclusion of the newest finished Pages build. |
| Last Pages build finished | 2026-10-22T08:56:59Z | The same run; its updated time. |
| Last leak-guard run | success | GitHub REST API, workflow runs; conclusion of the newest finished run of the guard workflow. |
What this page is not
It is a snapshot, not a feed. make stats runs the generator against the cluster, and
the file it writes is committed like any other change. Between runs the numbers stand still
while the estate does not. A guard fails the build when the file is more than three weeks
old, which keeps the timestamp honest; it cannot keep the figures current, only visible
when they are not.
It is aggregates only, and the platform is private. Counts, percentages, versions and dates. No machine, address, namespace, workload or secret is named, and the generator runs its own output through the same leak guard as every page on this site before it will write the file, and refuses to write at all if a measurement fails. A partial page would be worse than a stale one.
Measured is not the same as good. Each figure says what was counted, and that is all it says. A count of alert rules is not a measure of whether they would fire; a count of tests is not a pass rate; a green health word is a reading, not a verdict.
Some things are missing on purpose. Vulnerability counts by severity, the code-quality gate, the number of identity realms and the findings in the tracking systems all sit behind credentials that the generator does not hold. Nothing here guesses at them, and nothing is published for them. If a figure you expected is absent, that is the reason.