From 813148c4af3e2d358d037ea5cdf8d378ddfe76ea Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 6 May 2026 18:02:25 +0000 Subject: [PATCH 001/315] kms: switch to non-proxied DNS so port 1688 is reachable externally Cloudflare cannot proxy raw TCP/1688 (KMS protocol). Switch kms.viktorbarzin.me from CF-proxied CNAME to direct A/AAAA so clients can reach the vlmcsd LoadBalancer (10.0.20.200) via the existing pfSense WAN port-forward for 1688. Verified end-to-end: vlmcs against 176.12.22.76:1688 completes the KMS V4 handshake for Office Professional Plus 2019. Co-Authored-By: Claude Opus 4.7 --- stacks/kms/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stacks/kms/main.tf b/stacks/kms/main.tf index 1ad91cd2..3b758159 100644 --- a/stacks/kms/main.tf +++ b/stacks/kms/main.tf @@ -124,7 +124,7 @@ resource "kubernetes_service" "kms-web-page" { module "ingress" { source = "../../modules/kubernetes/ingress_factory" - dns_type = "proxied" + dns_type = "non-proxied" namespace = kubernetes_namespace.kms.metadata[0].name name = "kms" tls_secret_name = var.tls_secret_name From da7a11eb3bf2d0203eb9cc88cc8ad367d29b0a03 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Tue, 5 May 2026 22:51:19 +0100 Subject: [PATCH 002/315] fix: strip conditional headers in bot-block-proxy to fix CalDAV sync nginx's not_modified_filter evaluated If-Match headers forwarded by Traefik's forwardAuth, returning 412 and breaking CalDAV VTODO updates from macOS/iOS Reminders. Switch to OpenResty and clear conditional headers with Lua before proxy processing. --- stacks/traefik/modules/traefik/main.tf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/stacks/traefik/modules/traefik/main.tf b/stacks/traefik/modules/traefik/main.tf index 14d0e907..8be8b10a 100644 --- a/stacks/traefik/modules/traefik/main.tf +++ b/stacks/traefik/modules/traefik/main.tf @@ -307,6 +307,12 @@ resource "kubernetes_config_map" "bot_block_proxy_config" { server { listen 8080; location /auth { + access_by_lua_block { + ngx.req.clear_header("If-Match") + ngx.req.clear_header("If-None-Match") + ngx.req.clear_header("If-Modified-Since") + ngx.req.clear_header("If-Unmodified-Since") + } proxy_pass http://poison_fountain; proxy_connect_timeout 3s; proxy_read_timeout 5s; @@ -373,7 +379,7 @@ resource "kubernetes_deployment" "bot_block_proxy" { } container { name = "nginx" - image = "nginx:1-alpine" + image = "openresty/openresty:alpine" port { container_port = 8080 From 87069ae5c3b5c528e6205478732242bd09bf8529 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 6 May 2026 20:16:06 +0000 Subject: [PATCH 003/315] monitoring(wealth): add delta row (1d / 7d / 30d / 90d net-worth changes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New row at y=4 with 4 stat panels showing net-worth change over the trailing windows. Each uses the latest-per-account stitching pattern (skew-resilient against partial-day syncs) and computes: delta = SUM(latest per account) - SUM(latest per account at or before max_complete - N) Where max_complete is the most recent date all accounts have a row. For each window: 1d, 7d, 30d, 90d. Verified live values: +£8,575 / +£22,696 / +£144,633 / +£174,612. All panels at y >= 4 shifted down by 4 rows to make room (Net worth chart 4->8, Per-account stacked 24->28, Activity log 77->81, etc.). Note: this commit also reformats the dashboard JSON from compact- object form to indented form (json.dump indent=2 side effect from the Python patch script). No semantic changes outside the new panels and y-shifts. --- .../modules/monitoring/dashboards/wealth.json | 1056 ++++++++++++++--- 1 file changed, 914 insertions(+), 142 deletions(-) diff --git a/stacks/monitoring/modules/monitoring/dashboards/wealth.json b/stacks/monitoring/modules/monitoring/dashboards/wealth.json index c5719a53..e3c6849a 100644 --- a/stacks/monitoring/modules/monitoring/dashboards/wealth.json +++ b/stacks/monitoring/modules/monitoring/dashboards/wealth.json @@ -3,7 +3,10 @@ "list": [ { "builtIn": 1, - "datasource": {"type": "datasource", "uid": "grafana"}, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", @@ -11,7 +14,10 @@ "type": "dashboard" }, { - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "enable": true, "hide": false, "iconColor": "purple", @@ -36,12 +42,23 @@ "id": 1, "title": "Net worth (current)", "type": "stat", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 4, "w": 4, "x": 0, "y": 0}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "currencyGBP", - "color": {"mode": "fixed", "fixedColor": "green"}, + "color": { + "mode": "fixed", + "fixedColor": "green" + }, "decimals": 2 }, "overrides": [] @@ -51,13 +68,22 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", - "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, "textMode": "auto" }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -70,12 +96,23 @@ "title": "Net contribution (cumulative)", "description": "Total deposits minus withdrawals across all accounts.", "type": "stat", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 4, "w": 4, "x": 4, "y": 0}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "currencyGBP", - "color": {"mode": "fixed", "fixedColor": "blue"}, + "color": { + "mode": "fixed", + "fixedColor": "blue" + }, "decimals": 2 }, "overrides": [] @@ -85,13 +122,22 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", - "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, "textMode": "auto" }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -104,18 +150,34 @@ "title": "Growth (unrealised)", "description": "Net worth minus net contribution — the gain on everything you've put in.", "type": "stat", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 4, "w": 4, "x": 8, "y": 0}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "currencyGBP", - "color": {"mode": "thresholds"}, + "color": { + "mode": "thresholds" + }, "decimals": 2, "thresholds": { "mode": "absolute", "steps": [ - {"color": "red", "value": null}, - {"color": "green", "value": 0} + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } ] } }, @@ -126,13 +188,22 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", - "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, "textMode": "auto" }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -145,19 +216,38 @@ "title": "ROI %", "description": "Growth / net contribution × 100. Excludes accounts with zero/negative contribution (Schwab) to avoid distortion.", "type": "stat", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 4, "w": 3, "x": 12, "y": 0}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 12, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "percent", - "color": {"mode": "thresholds"}, + "color": { + "mode": "thresholds" + }, "decimals": 2, "thresholds": { "mode": "absolute", "steps": [ - {"color": "red", "value": null}, - {"color": "yellow", "value": 0}, - {"color": "green", "value": 5} + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 0 + }, + { + "color": "green", + "value": 5 + } ] } }, @@ -168,13 +258,22 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", - "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, "textMode": "auto" }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -187,11 +286,22 @@ "title": "Net worth — total over time", "description": "Daily total_value summed across all accounts (base GBP).", "type": "timeseries", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 10, "w": 24, "x": 0, "y": 4}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 8 + }, "fieldConfig": { "defaults": { - "color": {"mode": "fixed", "fixedColor": "green"}, + "color": { + "mode": "fixed", + "fixedColor": "green" + }, "unit": "currencyGBP", "decimals": 2, "custom": { @@ -202,24 +312,48 @@ "showPoints": "never", "spanNulls": true, "axisPlacement": "auto", - "stacking": {"group": "A", "mode": "none"} + "stacking": { + "group": "A", + "mode": "none" + } } }, "overrides": [ { - "matcher": {"id": "byName", "options": "net_worth"}, - "properties": [{"id": "displayName", "value": "Net worth"}] + "matcher": { + "id": "byName", + "options": "net_worth" + }, + "properties": [ + { + "id": "displayName", + "value": "Net worth" + } + ] } ] }, "options": { - "legend": {"calcs": ["last", "max"], "displayMode": "table", "placement": "bottom"}, - "tooltip": {"mode": "multi", "sort": "desc"} + "legend": { + "calcs": [ + "last", + "max" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "time_series", @@ -232,11 +366,21 @@ "title": "Net contribution vs market value", "description": "Net contribution = cumulative deposits − withdrawals. Market value = total_value (cash + investments). Gap between the two = unrealised growth.", "type": "timeseries", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 10, "w": 12, "x": 0, "y": 14}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 18 + }, "fieldConfig": { "defaults": { - "color": {"mode": "palette-classic"}, + "color": { + "mode": "palette-classic" + }, "unit": "currencyGBP", "decimals": 2, "custom": { @@ -247,34 +391,73 @@ "showPoints": "never", "spanNulls": true, "axisPlacement": "auto", - "stacking": {"group": "A", "mode": "none"} + "stacking": { + "group": "A", + "mode": "none" + } } }, "overrides": [ { - "matcher": {"id": "byName", "options": "market_value"}, + "matcher": { + "id": "byName", + "options": "market_value" + }, "properties": [ - {"id": "color", "value": {"mode": "fixed", "fixedColor": "green"}}, - {"id": "displayName", "value": "Market value"} + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "green" + } + }, + { + "id": "displayName", + "value": "Market value" + } ] }, { - "matcher": {"id": "byName", "options": "net_contribution"}, + "matcher": { + "id": "byName", + "options": "net_contribution" + }, "properties": [ - {"id": "color", "value": {"mode": "fixed", "fixedColor": "blue"}}, - {"id": "displayName", "value": "Net contribution"} + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "blue" + } + }, + { + "id": "displayName", + "value": "Net contribution" + } ] } ] }, "options": { - "legend": {"calcs": ["last"], "displayMode": "table", "placement": "bottom"}, - "tooltip": {"mode": "multi", "sort": "desc"} + "legend": { + "calcs": [ + "last" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "time_series", @@ -287,11 +470,22 @@ "title": "Growth (market value − contribution) over time", "description": "Unrealised gain across all accounts. Filled area to emphasise the wealth created above the contributed capital.", "type": "timeseries", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 10, "w": 12, "x": 12, "y": 14}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 18 + }, "fieldConfig": { "defaults": { - "color": {"mode": "fixed", "fixedColor": "#56A64B"}, + "color": { + "mode": "fixed", + "fixedColor": "#56A64B" + }, "unit": "currencyGBP", "decimals": 2, "custom": { @@ -303,24 +497,48 @@ "showPoints": "never", "spanNulls": true, "axisPlacement": "auto", - "stacking": {"group": "A", "mode": "none"} + "stacking": { + "group": "A", + "mode": "none" + } } }, "overrides": [ { - "matcher": {"id": "byName", "options": "growth"}, - "properties": [{"id": "displayName", "value": "Growth"}] + "matcher": { + "id": "byName", + "options": "growth" + }, + "properties": [ + { + "id": "displayName", + "value": "Growth" + } + ] } ] }, "options": { - "legend": {"calcs": ["last", "max"], "displayMode": "table", "placement": "bottom"}, - "tooltip": {"mode": "multi", "sort": "desc"} + "legend": { + "calcs": [ + "last", + "max" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "time_series", @@ -333,11 +551,21 @@ "title": "Per-account stacked — total value", "description": "Stacked area showing each account's contribution to total net worth over time. Useful for spotting which account drives the trajectory.", "type": "timeseries", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 11, "w": 24, "x": 0, "y": 24}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 28 + }, "fieldConfig": { "defaults": { - "color": {"mode": "palette-classic"}, + "color": { + "mode": "palette-classic" + }, "unit": "currencyGBP", "decimals": 2, "custom": { @@ -348,19 +576,34 @@ "showPoints": "never", "spanNulls": true, "axisPlacement": "auto", - "stacking": {"group": "A", "mode": "normal"} + "stacking": { + "group": "A", + "mode": "normal" + } } }, "overrides": [] }, "options": { - "legend": {"calcs": ["last"], "displayMode": "table", "placement": "bottom"}, - "tooltip": {"mode": "multi", "sort": "desc"} + "legend": { + "calcs": [ + "last" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "time_series", @@ -373,11 +616,21 @@ "title": "Cash vs invested (stacked)", "description": "Daily breakdown of uninvested broker cash vs market value of investments. WORKPLACE_PENSION accounts (Fidelity) are reclassified entirely as invested — Wealthfolio dumps pension wrappers into cash_balance because it doesn't track the underlying fund holdings, but they are not actually cash.", "type": "timeseries", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 10, "w": 24, "x": 0, "y": 35}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 39 + }, "fieldConfig": { "defaults": { - "color": {"mode": "palette-classic"}, + "color": { + "mode": "palette-classic" + }, "unit": "currencyGBP", "decimals": 2, "custom": { @@ -388,34 +641,73 @@ "showPoints": "never", "spanNulls": true, "axisPlacement": "auto", - "stacking": {"group": "A", "mode": "normal"} + "stacking": { + "group": "A", + "mode": "normal" + } } }, "overrides": [ { - "matcher": {"id": "byName", "options": "cash"}, + "matcher": { + "id": "byName", + "options": "cash" + }, "properties": [ - {"id": "color", "value": {"mode": "fixed", "fixedColor": "#FADE2A"}}, - {"id": "displayName", "value": "Cash"} + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "#FADE2A" + } + }, + { + "id": "displayName", + "value": "Cash" + } ] }, { - "matcher": {"id": "byName", "options": "invested"}, + "matcher": { + "id": "byName", + "options": "invested" + }, "properties": [ - {"id": "color", "value": {"mode": "fixed", "fixedColor": "#56A64B"}}, - {"id": "displayName", "value": "Invested"} + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "#56A64B" + } + }, + { + "id": "displayName", + "value": "Invested" + } ] } ] }, "options": { - "legend": {"calcs": ["last"], "displayMode": "table", "placement": "bottom"}, - "tooltip": {"mode": "multi", "sort": "desc"} + "legend": { + "calcs": [ + "last" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "time_series", @@ -428,27 +720,51 @@ "title": "Activity log", "description": "Recent activities (BUY / SELL / DEPOSIT / WITHDRAWAL / DIVIDEND / etc.) across all accounts. Limited to 100 most recent.", "type": "table", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 14, "w": 24, "x": 0, "y": 77}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 14, + "w": 24, + "x": 0, + "y": 81 + }, "fieldConfig": { "defaults": { - "custom": {"align": "auto", "displayMode": "auto"} + "custom": { + "align": "auto", + "displayMode": "auto" + } }, "overrides": [ { - "matcher": {"id": "byName", "options": "amount"}, - "properties": [{"id": "unit", "value": "currencyGBP"}] + "matcher": { + "id": "byName", + "options": "amount" + }, + "properties": [ + { + "id": "unit", + "value": "currencyGBP" + } + ] } ] }, "options": { "cellHeight": "sm", - "footer": {"show": false} + "footer": { + "show": false + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -461,19 +777,38 @@ "title": "12mo return", "description": "Modified-Dietz return over the trailing 12 months: market_gain / (nw_12mo_ago + 0.5 × contributions_12mo). Excludes new money in — answers 'how did my investments perform' rather than 'how much did my net worth change'.", "type": "stat", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 4, "w": 3, "x": 15, "y": 0}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 15, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "percent", - "color": {"mode": "thresholds"}, + "color": { + "mode": "thresholds" + }, "decimals": 2, "thresholds": { "mode": "absolute", "steps": [ - {"color": "red", "value": null}, - {"color": "yellow", "value": 0}, - {"color": "green", "value": 5} + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 0 + }, + { + "color": "green", + "value": 5 + } ] } }, @@ -484,13 +819,22 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", - "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, "textMode": "auto" }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -503,12 +847,23 @@ "title": "12mo contrib", "description": "Net contributions (deposits − withdrawals) over the trailing 12 months. How much new money you put in — independent of market movement.", "type": "stat", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 4, "w": 3, "x": 18, "y": 0}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 18, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "currencyGBP", - "color": {"mode": "fixed", "fixedColor": "blue"}, + "color": { + "mode": "fixed", + "fixedColor": "blue" + }, "decimals": 2 }, "overrides": [] @@ -518,13 +873,22 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", - "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, "textMode": "auto" }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -537,18 +901,34 @@ "title": "12mo gain", "description": "Trailing 12-month market gain in £ — the change in net worth minus net contributions. What the markets gave you, separate from money you added in.", "type": "stat", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 4, "w": 3, "x": 21, "y": 0}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 21, + "y": 0 + }, "fieldConfig": { "defaults": { "unit": "currencyGBP", - "color": {"mode": "thresholds"}, + "color": { + "mode": "thresholds" + }, "decimals": 2, "thresholds": { "mode": "absolute", "steps": [ - {"color": "red", "value": null}, - {"color": "green", "value": 0} + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } ] } }, @@ -559,13 +939,22 @@ "graphMode": "area", "justifyMode": "center", "orientation": "auto", - "reduceOptions": {"calcs": ["lastNotNull"], "fields": "", "values": false}, + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, "textMode": "auto" }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -573,24 +962,303 @@ } ] }, + { + "id": 17, + "title": "Δ 1d", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '1 day' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT (SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap) AS delta" + } + ] + }, + { + "id": 18, + "title": "Δ 7d", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '7 days' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT (SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap) AS delta" + } + ] + }, + { + "id": 19, + "title": "Δ 30d", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 12, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '30 days' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT (SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap) AS delta" + } + ] + }, + { + "id": 20, + "title": "Δ 90d", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 18, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '90 days' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT (SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap) AS delta" + } + ] + }, { "id": 12, "title": "Yearly investment return %", "description": "Modified-Dietz return per calendar year: market_gain / (nw_start + 0.5 × contributions). Pure investment performance — excludes new contributions, so a £100k vest doesn't show as 100% growth. Negative bars = market losses (e.g., 2022 bear market).", "type": "barchart", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 11, "w": 24, "x": 0, "y": 45}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 49 + }, "fieldConfig": { "defaults": { - "color": {"mode": "thresholds"}, + "color": { + "mode": "thresholds" + }, "unit": "percent", "decimals": 1, "thresholds": { "mode": "absolute", "steps": [ - {"color": "red", "value": null}, - {"color": "yellow", "value": 0}, - {"color": "green", "value": 5} + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 0 + }, + { + "color": "green", + "value": 5 + } ] }, "custom": { @@ -603,9 +1271,15 @@ }, "overrides": [ { - "matcher": {"id": "byName", "options": "year"}, + "matcher": { + "id": "byName", + "options": "year" + }, "properties": [ - {"id": "unit", "value": "string"} + { + "id": "unit", + "value": "string" + } ] } ] @@ -619,13 +1293,22 @@ "stacking": "none", "xField": "year", "xTickLabelRotation": 0, - "legend": {"displayMode": "list", "placement": "bottom"}, - "tooltip": {"mode": "single", "sort": "none"} + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -638,11 +1321,21 @@ "title": "Annual change decomposition — contributions vs market gain", "description": "Each calendar year's net worth change split into 'new money in' (contributions − withdrawals) and 'market gain' (everything else: price appreciation, dividends, etc.). Shows whether you grew because you saved or because the market did the work. Negative bars = withdrawals or market losses.", "type": "barchart", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 11, "w": 24, "x": 0, "y": 56}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 60 + }, "fieldConfig": { "defaults": { - "color": {"mode": "palette-classic"}, + "color": { + "mode": "palette-classic" + }, "unit": "currencyGBP", "decimals": 2, "custom": { @@ -655,23 +1348,53 @@ }, "overrides": [ { - "matcher": {"id": "byName", "options": "year"}, + "matcher": { + "id": "byName", + "options": "year" + }, "properties": [ - {"id": "unit", "value": "string"} + { + "id": "unit", + "value": "string" + } ] }, { - "matcher": {"id": "byName", "options": "contributions"}, + "matcher": { + "id": "byName", + "options": "contributions" + }, "properties": [ - {"id": "color", "value": {"mode": "fixed", "fixedColor": "blue"}}, - {"id": "displayName", "value": "Net contributions"} + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "blue" + } + }, + { + "id": "displayName", + "value": "Net contributions" + } ] }, { - "matcher": {"id": "byName", "options": "market_gain"}, + "matcher": { + "id": "byName", + "options": "market_gain" + }, "properties": [ - {"id": "color", "value": {"mode": "fixed", "fixedColor": "#56A64B"}}, - {"id": "displayName", "value": "Market gain"} + { + "id": "color", + "value": { + "mode": "fixed", + "fixedColor": "#56A64B" + } + }, + { + "id": "displayName", + "value": "Market gain" + } ] } ] @@ -685,13 +1408,25 @@ "stacking": "normal", "xField": "year", "xTickLabelRotation": 0, - "legend": {"calcs": ["sum"], "displayMode": "table", "placement": "bottom"}, - "tooltip": {"mode": "multi", "sort": "desc"} + "legend": { + "calcs": [ + "sum" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -704,19 +1439,38 @@ "title": "Per-account ROI %", "description": "(market value − net contribution) / net contribution × 100, latest snapshot. Excludes accounts with zero/negative net contribution (Schwab — RSU vests sold = negative contribution distorts the ratio). Pension shows 0% because Wealthfolio doesn't track underlying fund holdings, so cost_basis = 0 and 'growth' is just the cash balance reported.", "type": "barchart", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, - "gridPos": {"h": 10, "w": 24, "x": 0, "y": 67}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 71 + }, "fieldConfig": { "defaults": { - "color": {"mode": "thresholds"}, + "color": { + "mode": "thresholds" + }, "unit": "percent", "decimals": 2, "thresholds": { "mode": "absolute", "steps": [ - {"color": "red", "value": null}, - {"color": "yellow", "value": 0}, - {"color": "green", "value": 10} + { + "color": "red", + "value": null + }, + { + "color": "yellow", + "value": 0 + }, + { + "color": "green", + "value": 10 + } ] }, "custom": { @@ -737,13 +1491,22 @@ "showValue": "always", "stacking": "none", "xField": "account", - "legend": {"displayMode": "list", "placement": "bottom"}, - "tooltip": {"mode": "single", "sort": "none"} + "legend": { + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single", + "sort": "none" + } }, "targets": [ { "refId": "A", - "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, "rawQuery": true, "editorMode": "code", "format": "table", @@ -754,9 +1517,18 @@ ], "refresh": "5m", "schemaVersion": 39, - "tags": ["finance", "personal", "wealth"], - "templating": {"list": []}, - "time": {"from": "now-180d", "to": "now"}, + "tags": [ + "finance", + "personal", + "wealth" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-180d", + "to": "now" + }, "timepicker": {}, "timezone": "browser", "title": "Wealth", From 0f107aeacb4d45be9fa439874c83dbfba4329e5b Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 6 May 2026 20:25:33 +0000 Subject: [PATCH 004/315] monitoring(wealth): pair every delta panel with market-only twin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User feedback: net-worth delta panels (1d/7d/30d/90d) confused because +£174k over 90d looked too big against the £271k cumulative unrealised gain. Decomposition showed the 90d delta was £114k of new money in (contributions) + £60k of actual market gain. So now the delta row shows BOTH: Δ Nd (all) — net-worth change incl new money (the original number) Δ Nd (mkt) — pure market gain, contributions stripped out Pattern for "(mkt)" panels: same now_snap / past_snap CTEs but selecting both total_value and net_contribution, then computing (nw_delta - contrib_delta) = market_gain over window. Layout: 8 panels at w=3 each on the y=4 row, paired by window (all next to mkt for each timeframe), so you can see "wealth change vs investment performance" at a glance. Verified live (90d): all=+£174,612, mkt=+£60,343, contrib=+£114,268. --- .../modules/monitoring/dashboards/wealth.json | 290 +++++++++++++++++- 1 file changed, 279 insertions(+), 11 deletions(-) diff --git a/stacks/monitoring/modules/monitoring/dashboards/wealth.json b/stacks/monitoring/modules/monitoring/dashboards/wealth.json index e3c6849a..4132bd9f 100644 --- a/stacks/monitoring/modules/monitoring/dashboards/wealth.json +++ b/stacks/monitoring/modules/monitoring/dashboards/wealth.json @@ -964,7 +964,8 @@ }, { "id": 17, - "title": "Δ 1d", + "title": "Δ 1d (all)", + "description": "Net worth delta over the trailing window (latest snapshot minus snapshot from N days ago). Includes new money paid in (salary, vests, deposits) AND market gains.", "type": "stat", "datasource": { "type": "grafana-postgresql-datasource", @@ -972,7 +973,7 @@ }, "gridPos": { "h": 4, - "w": 6, + "w": 3, "x": 0, "y": 4 }, @@ -1028,8 +1029,9 @@ ] }, { - "id": 18, - "title": "Δ 7d", + "id": 21, + "title": "Δ 1d (mkt)", + "description": "Pure market gain over the trailing window — net-worth delta minus net-contribution delta. Excludes new money paid in. Apples-to-apples with the Growth panel.", "type": "stat", "datasource": { "type": "grafana-postgresql-datasource", @@ -1037,7 +1039,73 @@ }, "gridPos": { "h": 4, - "w": 6, + "w": 3, + "x": 3, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '1 day' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT ((SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap)) - ((SELECT SUM(net_contribution) FROM now_snap) - (SELECT SUM(net_contribution) FROM past_snap)) AS market_delta" + } + ] + }, + { + "id": 18, + "title": "Δ 7d (all)", + "description": "Net worth delta over the trailing window (latest snapshot minus snapshot from N days ago). Includes new money paid in (salary, vests, deposits) AND market gains.", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, "x": 6, "y": 4 }, @@ -1093,8 +1161,9 @@ ] }, { - "id": 19, - "title": "Δ 30d", + "id": 22, + "title": "Δ 7d (mkt)", + "description": "Pure market gain over the trailing window — net-worth delta minus net-contribution delta. Excludes new money paid in. Apples-to-apples with the Growth panel.", "type": "stat", "datasource": { "type": "grafana-postgresql-datasource", @@ -1102,7 +1171,73 @@ }, "gridPos": { "h": 4, - "w": 6, + "w": 3, + "x": 9, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '7 days' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT ((SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap)) - ((SELECT SUM(net_contribution) FROM now_snap) - (SELECT SUM(net_contribution) FROM past_snap)) AS market_delta" + } + ] + }, + { + "id": 19, + "title": "Δ 30d (all)", + "description": "Net worth delta over the trailing window (latest snapshot minus snapshot from N days ago). Includes new money paid in (salary, vests, deposits) AND market gains.", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, "x": 12, "y": 4 }, @@ -1158,8 +1293,9 @@ ] }, { - "id": 20, - "title": "Δ 90d", + "id": 23, + "title": "Δ 30d (mkt)", + "description": "Pure market gain over the trailing window — net-worth delta minus net-contribution delta. Excludes new money paid in. Apples-to-apples with the Growth panel.", "type": "stat", "datasource": { "type": "grafana-postgresql-datasource", @@ -1167,7 +1303,73 @@ }, "gridPos": { "h": 4, - "w": 6, + "w": 3, + "x": 15, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '30 days' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT ((SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap)) - ((SELECT SUM(net_contribution) FROM now_snap) - (SELECT SUM(net_contribution) FROM past_snap)) AS market_delta" + } + ] + }, + { + "id": 20, + "title": "Δ 90d (all)", + "description": "Net worth delta over the trailing window (latest snapshot minus snapshot from N days ago). Includes new money paid in (salary, vests, deposits) AND market gains.", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, "x": 18, "y": 4 }, @@ -1222,6 +1424,72 @@ } ] }, + { + "id": 24, + "title": "Δ 90d (mkt)", + "description": "Pure market gain over the trailing window — net-worth delta minus net-contribution delta. Excludes new money paid in. Apples-to-apples with the Growth panel.", + "type": "stat", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 21, + "y": 4 + }, + "fieldConfig": { + "defaults": { + "unit": "currencyGBP", + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "targets": [ + { + "refId": "A", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "wealth-pg" + }, + "rawQuery": true, + "editorMode": "code", + "format": "table", + "rawSql": "WITH active_count AS (SELECT COUNT(*) AS n FROM accounts), max_complete AS (SELECT MAX(valuation_date) AS d FROM (SELECT d.valuation_date, COUNT(*) AS c FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date) x WHERE c >= (SELECT n FROM active_count)), now_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC), past_snap AS (SELECT DISTINCT ON (d.account_id) d.total_value, d.net_contribution FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id WHERE d.valuation_date <= (SELECT d - INTERVAL '90 days' FROM max_complete) ORDER BY d.account_id, d.valuation_date DESC) SELECT ((SELECT SUM(total_value) FROM now_snap) - (SELECT SUM(total_value) FROM past_snap)) - ((SELECT SUM(net_contribution) FROM now_snap) - (SELECT SUM(net_contribution) FROM past_snap)) AS market_delta" + } + ] + }, { "id": 12, "title": "Yearly investment return %", From f006b485669c2f65dd69add4bb337d11559eabbf Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 6 May 2026 20:29:27 +0000 Subject: [PATCH 005/315] monitoring(wealth): delta panels to 2x4 grid (rows = type, cols = window) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Better visual grouping: instead of 8 paired panels in a single row at w=3 (cramped, hard to scan), arrange as a 2x4 grid at w=6. Top row ("all" — wealth change incl new money), bottom row ("mkt" — pure market gain). Columns are timeframes 1d / 7d / 30d / 90d. Reading vertically: same window, two interpretations side by side. Reading horizontally: same metric across timeframes. Layout shift: delta row goes from y=4 (4 wide) to y=4..11 (8 high). All chart/log panels with y >= 8 shift down by another 4 rows (net-worth chart 8->12, activity log 81->85, etc.). --- .../modules/monitoring/dashboards/wealth.json | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/stacks/monitoring/modules/monitoring/dashboards/wealth.json b/stacks/monitoring/modules/monitoring/dashboards/wealth.json index 4132bd9f..54d25c4b 100644 --- a/stacks/monitoring/modules/monitoring/dashboards/wealth.json +++ b/stacks/monitoring/modules/monitoring/dashboards/wealth.json @@ -294,7 +294,7 @@ "h": 10, "w": 24, "x": 0, - "y": 8 + "y": 12 }, "fieldConfig": { "defaults": { @@ -374,7 +374,7 @@ "h": 10, "w": 12, "x": 0, - "y": 18 + "y": 22 }, "fieldConfig": { "defaults": { @@ -478,7 +478,7 @@ "h": 10, "w": 12, "x": 12, - "y": 18 + "y": 22 }, "fieldConfig": { "defaults": { @@ -559,7 +559,7 @@ "h": 11, "w": 24, "x": 0, - "y": 28 + "y": 32 }, "fieldConfig": { "defaults": { @@ -624,7 +624,7 @@ "h": 10, "w": 24, "x": 0, - "y": 39 + "y": 43 }, "fieldConfig": { "defaults": { @@ -728,7 +728,7 @@ "h": 14, "w": 24, "x": 0, - "y": 81 + "y": 85 }, "fieldConfig": { "defaults": { @@ -973,7 +973,7 @@ }, "gridPos": { "h": 4, - "w": 3, + "w": 6, "x": 0, "y": 4 }, @@ -1039,9 +1039,9 @@ }, "gridPos": { "h": 4, - "w": 3, - "x": 3, - "y": 4 + "w": 6, + "x": 0, + "y": 8 }, "fieldConfig": { "defaults": { @@ -1105,7 +1105,7 @@ }, "gridPos": { "h": 4, - "w": 3, + "w": 6, "x": 6, "y": 4 }, @@ -1171,9 +1171,9 @@ }, "gridPos": { "h": 4, - "w": 3, - "x": 9, - "y": 4 + "w": 6, + "x": 6, + "y": 8 }, "fieldConfig": { "defaults": { @@ -1237,7 +1237,7 @@ }, "gridPos": { "h": 4, - "w": 3, + "w": 6, "x": 12, "y": 4 }, @@ -1303,9 +1303,9 @@ }, "gridPos": { "h": 4, - "w": 3, - "x": 15, - "y": 4 + "w": 6, + "x": 12, + "y": 8 }, "fieldConfig": { "defaults": { @@ -1369,7 +1369,7 @@ }, "gridPos": { "h": 4, - "w": 3, + "w": 6, "x": 18, "y": 4 }, @@ -1435,9 +1435,9 @@ }, "gridPos": { "h": 4, - "w": 3, - "x": 21, - "y": 4 + "w": 6, + "x": 18, + "y": 8 }, "fieldConfig": { "defaults": { @@ -1503,7 +1503,7 @@ "h": 11, "w": 24, "x": 0, - "y": 49 + "y": 53 }, "fieldConfig": { "defaults": { @@ -1597,7 +1597,7 @@ "h": 11, "w": 24, "x": 0, - "y": 60 + "y": 64 }, "fieldConfig": { "defaults": { @@ -1715,7 +1715,7 @@ "h": 10, "w": 24, "x": 0, - "y": 71 + "y": 75 }, "fieldConfig": { "defaults": { From 8b180f766235f25d8a0157eeed18dc0d40542e38 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 6 May 2026 20:35:38 +0000 Subject: [PATCH 006/315] openclaw: switch primary model to qwen3-coder-480b (qwen3.5-397b dead on NIM) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NVIDIA retired nim/qwen/qwen3.5-397b-a17b — modelrelay shows consistent TIMEOUTs over 24h+ of pings, and nim/nvidia/llama-3.1-nemotron-ultra-253b-v1 returns 404. With both gone the openclaw failover never reached mistral-large-3 in time, so every message hung until the 120s embedded-run timeout. Promote qwen3-coder-480b-a35b-instruct (already in models list, UP ~1-2s, 256k ctx) to primary; drop the dead nemotron-ultra fallback. --- stacks/openclaw/main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stacks/openclaw/main.tf b/stacks/openclaw/main.tf index 5d5f9a1d..11d129ee 100644 --- a/stacks/openclaw/main.tf +++ b/stacks/openclaw/main.tf @@ -131,8 +131,8 @@ resource "kubernetes_config_map" "openclaw_config" { mode = "off" } model = { - primary = "nim/qwen/qwen3.5-397b-a17b" - fallbacks = ["nim/mistralai/mistral-large-3-675b-instruct-2512", "nim/nvidia/llama-3.1-nemotron-ultra-253b-v1", "modelrelay/auto-fastest"] + primary = "nim/qwen/qwen3-coder-480b-a35b-instruct" + fallbacks = ["nim/mistralai/mistral-large-3-675b-instruct-2512", "modelrelay/auto-fastest"] } models = { "modelrelay/auto-fastest" = {} From f90d79ed4e7d0877a1fc65e1e0ad39280041da6e Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 6 May 2026 21:00:07 +0000 Subject: [PATCH 007/315] f1-stream: only show streams confirmed playable by headless browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cuts the stream list from 23 mostly-broken entries to ~6 confirmed-playable ones, and adds an iframe-stripping proxy so embed sources (hmembeds, etc.) load through our origin without X-Frame-Options / CSP / JS frame-buster blocks. Why: the previous list was dominated by Discord-shared news article URLs, hardcoded aggregator landing pages, and other non-stream URLs that all sat at is_live=true because embed streams skipped the health check entirely. Users could not tell which links would actually play. What: - backend/playback_verifier.py: new headless-Chromium verifier (Playwright) that polls each candidate stream for a codec-independent "playable" signal (hls.js MANIFEST_PARSED for m3u8;