Single-binary self-hosted uptime monitoring and status pages on Rust axum: HTTP probes, Lighthouse audits, SEO crawler, and PDF reports.
axumdockerrustself-hostedsqlitestatus-pageuptime-monitoringvite
1{% extends "base.html" %}
2
3{% block extra_css %}
4<link rel="stylesheet" href="{{ vite_asset('static_src/pages/index.js', 'css') }}">
5{% endblock %}
6
7{% block breadcrumbs %}
8<nav aria-label="breadcrumb">
9 <ol class="breadcrumb mb-0">
10 <li class="breadcrumb-item"><a href="/">Home</a></li>
11 <li class="breadcrumb-item active" aria-current="page">{{ title }}</li>
12 </ol>
13</nav>
14{% endblock %}
15
16{% block main %}
17<div class="container my-5">
18 <div class="row">
19 <div class="col-lg-8 offset-lg-2">
20 <div class="section-label mb-2">release log</div>
21 <h1 class="fw-bolder text-white" style="letter-spacing: -0.01em;">{{ title }}</h1>
22 <p class="text-muted">Shipped changes, incidents, and the occasional tease for something coming.</p>
23 </div>
24 </div>
25
26 <div class="row mt-4">
27 <div class="col-lg-8 offset-lg-2">
28
29 <div class="changelog-entry">
30 <div class="changelog-date">2026-05-09</div>
31 <ul>
32 <li>Rewrite the entire backend from Django to a single statically-linked axum binary in Rust. sqlx + SQLite directly, minijinja for templates, no more Gunicorn or separate scheduler process. Property UUIDs are preserved so existing public status URLs keep working</li>
33 <li>Drop the multi-user accounts model. Auth is now a single operator password from <code>.env</code> with signed-cookie sessions and <code>SameSite=Strict</code> instead of CSRF tokens</li>
34 <li>Port the SEO crawler from <code>requests</code> + <code>BeautifulSoup</code> to <code>reqwest</code> + <code>scraper</code>. The 38 ranked checks (SEO, links, sitemap, accessibility, content, performance, security) match the original Python output</li>
35 <li>Switch alert email to <code>lettre</code> + <code>hickory-resolver</code>, still opportunistic STARTTLS direct-to-MX on port 25; Discord webhooks are plain HTTP POST</li>
36 <li>Add a <code>./status migrate <django.sqlite3></code> subcommand that imports an existing Django SQLite into the new schema while preserving Property UUIDs</li>
37 <li>Run the lighthouse CLI through <code>bun run --bun</code> so the runtime image drops nodejs and npm; chromium stays for lighthouse only</li>
38 <li>Probe Content-Encoding with a non-decompressing client so <code>response_ms</code> reflects the wire payload, not the decoded body</li>
39 <li>Fix the mobile nav menu being clipped by the fixed navbar height</li>
40 </ul>
41 </div>
42
43 <div class="changelog-entry">
44 <div class="changelog-date">2026-04-26</div>
45 <ul>
46 <li>Drop SQLite <code>mmap_size</code> to avoid a multi-process corruption hazard</li>
47 </ul>
48 </div>
49
50 <div class="changelog-entry">
51 <div class="changelog-date">2026-04-18</div>
52 <ul>
53 <li>Centralize SQLite settings and expand PRAGMA tuning (WAL, synchronous, cache, mmap, temp_store)</li>
54 <li>Commit alert state before firing notifications so a crash mid-alert can't cause duplicate sends</li>
55 <li>Harden the chromium wrapper with timeouts, <code>/tmp</code> temp files, and <code>--headless=new</code></li>
56 <li>Redesign alert emails to match the warm-earth site palette</li>
57 <li>Stop wedge-reset from failing a healthy queued backlog of checks</li>
58 </ul>
59 </div>
60
61 <div class="changelog-entry">
62 <div class="changelog-date">2026-04-17</div>
63 <ul>
64 <li>Redesign the whole dashboard with a warm-earth palette: mossy forest green and amber on deep charcoal, Monaspace Argon throughout</li>
65 <li>Rework the property page with live-signals tiles, Lighthouse score cards, styled Chart.js panels, and compact insight tables</li>
66 <li>Add recent-uptime bar strips and rolling uptime percentage to every property row on the dashboard</li>
67 <li>Surface the full Lighthouse performance breakdown: weighted metric tiles and top savings opportunities, ranked by impact</li>
68 <li>Swap the rocket emoji favicon for a themed SVG tile matching the in-app logo</li>
69 <li>Update the chromium wrapper to also pick up <code>google-chrome</code> so the webdev container runs locally without extra setup</li>
70 <li>Fix logout 405 by switching to a POST form + CSRF (Django 5 <code>LogoutView</code> is POST-only)</li>
71 <li>Refactor the scheduler with thread pools and graceful shutdown</li>
72 <li>Collapse the web and scheduler services into a single container via a Python supervisor in <code>entrypoint.py</code></li>
73 <li>Replace the Exim relay with a Python direct-to-MX email backend</li>
74 <li>Set <code>PYTHONUNBUFFERED</code> so scheduler output reaches <code>docker logs</code></li>
75 </ul>
76 </div>
77
78 <div class="changelog-entry">
79 <div class="changelog-date">2026-04-16</div>
80 <ul>
81 <li>Add live crawl/lighthouse status panel with real-time recrawl and rerun buttons</li>
82 <li>Rewrite the SEO crawler in-process using <code>requests</code> + <code>BeautifulSoup</code>, no more Scrapy subprocess</li>
83 <li>Harden the scheduler, alert state machine, and crawl pipeline against races and partial failures</li>
84 <li>Harden the Lighthouse runner and surface failure reasons in the UI</li>
85 <li>Migrate the frontend build from Webpack/Yarn to Vite/Bun</li>
86 </ul>
87 </div>
88
89 <div class="changelog-entry">
90 <div class="changelog-date">2026-04-09</div>
91 <ul>
92 <li>Migrate Python package management from Pipenv/pyenv to <code>uv</code></li>
93 </ul>
94 </div>
95
96 <div class="changelog-entry">
97 <div class="changelog-date">2026-04-03</div>
98 <ul>
99 <li>Bind the Docker port to localhost only, host should proxy in</li>
100 </ul>
101 </div>
102
103 <div class="changelog-entry">
104 <div class="changelog-date">2025-06-01</div>
105 <ul>
106 <li>Refactor property email templates to share a base</li>
107 </ul>
108 </div>
109
110 <div class="changelog-entry">
111 <div class="changelog-date">2025-05-31</div>
112 <ul>
113 <li>Improve alert system with state tracking and recovery notifications, no more alert spam on flapping sites</li>
114 </ul>
115 </div>
116
117 <div class="changelog-entry">
118 <div class="changelog-date">2022-07-25</div>
119 <ul>
120 <li>Add recrawl button</li>
121 <li>Run crawler weekly on a schedule</li>
122 </ul>
123 </div>
124
125 <div class="changelog-entry">
126 <div class="changelog-date">2022-07-23</div>
127 <ul>
128 <li>Add crawler insights: title, description, canonical, OG tags, H1 extraction per page</li>
129 </ul>
130 </div>
131
132 <div class="changelog-entry">
133 <div class="changelog-date">2022-07-16</div>
134 <ul>
135 <li>Add docker init to handle Lighthouse Chrome zombies</li>
136 </ul>
137 </div>
138
139 <div class="changelog-entry">
140 <div class="changelog-date">2022-07-15</div>
141 <ul>
142 <li>Add check queue to be more resource friendly</li>
143 <li>Add Lighthouse scores</li>
144 </ul>
145 </div>
146
147 <div class="changelog-entry">
148 <div class="changelog-date">2022-07-04</div>
149 <ul>
150 <li>Add properties listing pagination</li>
151 <li>Add properties listing search</li>
152 <li>Improve properties listing UI</li>
153 </ul>
154 </div>
155
156 <div class="changelog-entry">
157 <div class="changelog-date">2022-06-26</div>
158 <ul>
159 <li>Add HSTS monitoring</li>
160 </ul>
161 </div>
162
163 <div class="changelog-entry">
164 <div class="changelog-date">2022-06-25</div>
165 <ul>
166 <li>Add uptime chart</li>
167 </ul>
168 </div>
169
170 <div class="changelog-entry">
171 <div class="changelog-date">2022-06-19</div>
172 <ul>
173 <li>Create status project</li>
174 </ul>
175 </div>
176
177 </div>
178 </div>
179</div>
180{% endblock %}