Compare commits
16
Commits
bc9a133ee6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6b36fa235 | ||
|
|
180d58e646 | ||
|
|
fbaf874f8d | ||
|
|
70bb5a4e07 | ||
|
|
27a483a86e | ||
|
|
98d0d24318 | ||
|
|
af987eb9e8 | ||
|
|
cbc80cf903 | ||
|
|
a3f64d44d9 | ||
|
|
70c37bc1b6 | ||
|
|
f3b1843321 | ||
|
|
3ca57be97c | ||
|
|
d703f19ccc | ||
|
|
f2ab7f55f1 | ||
|
|
7a9f4b6bc7 | ||
|
|
fa2435388a |
@@ -1,3 +1,5 @@
|
||||
<a name="readme-top"></a>
|
||||
|
||||
# Utah Vehicle Health
|
||||
|
||||
Kevin Bell's summer 2026 development prototype for the first-attempt outcome of
|
||||
@@ -28,7 +30,7 @@ Start with the [final report](docs/final_report.md). Supporting deliverables:
|
||||
- [Detailed development results](docs/development_results.md)
|
||||
- [Model card](docs/model_card.md)
|
||||
- [Static dashboard specification](docs/dashboard_spec.md)
|
||||
- [Private-to-public architecture](docs/architecture.mmd)
|
||||
- [Private-to-public architecture](docs/architecture.md)
|
||||
- [10-minute presentation outline](docs/presentation_outline.md)
|
||||
- [Dashboard demo script](docs/demo_script.md)
|
||||
|
||||
@@ -150,6 +152,12 @@ repository root. Stop any older dashboard process with `Ctrl-C` first:
|
||||
node dashboard/server.mjs --lan
|
||||
```
|
||||
|
||||
On macOS, to prevent the host from sleeping while the demo runs:
|
||||
|
||||
```bash
|
||||
caffeinate -i node dashboard/server.mjs --lan
|
||||
```
|
||||
|
||||
The server prints a **This computer** URL and one or more interface-labeled
|
||||
**Other devices** URLs. Open the URL for the shared Wi-Fi or Ethernet interface
|
||||
on the second computer; do not use `0.0.0.0` as the browser address. Confirm the
|
||||
@@ -162,13 +170,6 @@ page, so the dashboard includes a dependency-free checksum implementation for
|
||||
that case. These checks validate bundle consistency; plain HTTP does not
|
||||
authenticate the network transport, so use a trusted demo network.
|
||||
|
||||
## Public hosting is not required
|
||||
|
||||
The presentation plan is the same-network live demo above. Nothing needs to be
|
||||
uploaded or published, and the server exposes only the dashboard's explicit
|
||||
static-file allowlist. If public hosting is ever reconsidered, publish only a
|
||||
separately reviewed copy of the `dashboard/` directory—never the private source
|
||||
repository.
|
||||
|
||||
## Author
|
||||
|
||||
@@ -177,3 +178,5 @@ Kevin Bell
|
||||
<kevinBell@Linux.com>
|
||||
|
||||
<https://kevinbell.dev/>
|
||||
|
||||
<p align="left"><a href="#readme-top">back to top</a></p>
|
||||
@@ -41,6 +41,10 @@ address, not the address to put in a browser. Confirm the in-app status reads
|
||||
**Validated sample aggregates**, then stop the server with `Ctrl-C` after the
|
||||
demo.
|
||||
|
||||
If the host is on a Tailscale tailnet, the server also prints a **Tailscale
|
||||
(tunnel)** URL (a `100.x.y.z` address). Open that URL on any other device signed
|
||||
into the same tailnet to reach the dashboard remotely, without sharing a LAN.
|
||||
|
||||
All data-contract and checksum validation remains enabled over LAN HTTP. When a
|
||||
remote browser does not expose the SubtleCrypto digest API to the plain-HTTP
|
||||
page, the dashboard uses its dependency-free SHA-256 implementation and still
|
||||
|
||||
+1
-1
@@ -86,8 +86,8 @@ function showRoute({ announce = true, focus = false } = {}) {
|
||||
if (heading) {
|
||||
heading.setAttribute("tabindex", "-1");
|
||||
heading.focus({ preventScroll: true });
|
||||
heading.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,12 +52,13 @@ function safeFile(requestUrl) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tailscale and similar VPN tunnels show up as extra "LAN" interfaces but are not
|
||||
// useful for an in-room demo, so keep them out of the printed URL list.
|
||||
function isTunnelInterface(interfaceName, address) {
|
||||
if (/^(utun|tailscale|tun|ppp|ipsec)/i.test(interfaceName)) return true;
|
||||
// A tailnet advertises the host over a 100.64.0.0/10 address, so label that URL
|
||||
// clearly for remote (off-LAN) demos over the tunnel.
|
||||
function interfaceLabel(interfaceName, address) {
|
||||
const [first, second] = address.split(".").map(Number);
|
||||
return first === 100 && second >= 64 && second <= 127; // 100.64.0.0/10 (CGNAT/tailnet)
|
||||
const isTailnet =
|
||||
/tailscale/i.test(interfaceName) || (first === 100 && second >= 64 && second <= 127);
|
||||
return isTailnet ? "Remote (tunnel)" : `Other devices (${interfaceName})`;
|
||||
}
|
||||
|
||||
function displayUrls(host, port) {
|
||||
@@ -72,10 +73,9 @@ function displayUrls(host, port) {
|
||||
for (const [interfaceName, addresses] of Object.entries(networkInterfaces())) {
|
||||
for (const address of addresses || []) {
|
||||
if (address.family !== "IPv4" || address.internal || seen.has(address.address)) continue;
|
||||
if (isTunnelInterface(interfaceName, address.address)) continue;
|
||||
seen.add(address.address);
|
||||
entries.push({
|
||||
label: `Other devices (${interfaceName})`,
|
||||
label: interfaceLabel(interfaceName, address.address),
|
||||
url: `http://${address.address}:${port}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## System Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
SOURCE[(Private inspection histories<br/>read-only source)]
|
||||
SAMPLE[Private page-sampled histories<br/>up to 10,000 vehicles<br/>keyed vehicle tokens]
|
||||
@@ -82,3 +85,4 @@ flowchart TD
|
||||
class LOGISTIC,TREE model
|
||||
class PUBLIC,CONTRACT,SITE,VIEWS,DEMO public
|
||||
class GUARDRAILS warning
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user