Skip to content

impl(dns-registry): recurring DNS backup job, renewal/cost tracking, dashboard view of the domain register #902

Open

Description

@dezren39

Context

Drew's direction (2026-10-25): a standing registry of every domain/DNS name we own, a regular backup of the full DNS config for every zone, and one place to see which domain renews next, which are completely unused, and which are unused but earmarked for projects.

First pass is DONE (2026-10-25) — this issue covers only the recurring machinery that pass deliberately did not build.

  • Register: ~/workspace/goals/stand-up-lab-cluster-talm-talos-cozystack-postgres-rustfs/files/dns-domain-register.md — 41 domains (39 in the Porkbun account developingtoday, + drewrypope.com and boats.dev which are Porkbun-registered under a different account), sorted by next renewal, with registrar, DNS host, renewal date, auto-renew state, list renewal price, where-used evidence, plan/status, and a review flag per domain.
  • Seed snapshot: ~/workspace/goals/stand-up-lab-cluster-talm-talos-cozystack-postgres-rustfs/hidden_files/dns-backup-2026-10-25/ — Cloudflare zone export (1 zone: security.cab, all 21 records), Porkbun account inventory (39 domains) + full TLD pricing table, public-DNS probe for all 41 domains, RDAP for the 2 outside-account domains. The snapshot README documents coverage and gaps precisely.

Headline facts from the first pass: all 39 account domains have auto-renew ON ($595.62/yr at list prices); next renewal is 1110x.de on 2027-02-09; only 3 of 41 domains show genuine use (security.cab, developing-today.com, drewrypope.com).

To build

1. Scheduled DNS-config backup job

Recurring (suggest weekly) read-only export producing a dated snapshot in the seed's format, diffed against the previous snapshot, surfacing: record changes, new/removed domains, expiry-date changes, price changes. Sources: Cloudflare API (zones + records; tokens in Bitwarden as cloudflare.api-token-day-to-day / cloudflare.api-token-account-wide) and Porkbun API (porkbun.api-key / porkbun.secret-key in Bitwarden).

2. Renewal & cost tracking over time

Each cycle appends to the register (or a history file beside it): observation date, per-domain renewal date, per-TLD list renewal price. This is how prior-year costs accumulate — the first pass could only mark them "unknown" (no invoice source reachable). Alert when a domain enters its renewal window (suggest ≤45 days; with auto-renew universal, the alert is about upcoming spend, not lapse risk) and when a renewal price changes cycle-over-cycle. If an invoice-grade source becomes available (Porkbun billing history, Gmail receipts), record actual charged amounts alongside list prices.

3. Dashboard / aggregator view

A view of the register data showing Drew's three lists: next renewal (with date + price), completely unused, unused but earmarked — fed by a structured export (e.g. register.json) the backup job emits alongside the Markdown register, consumed by the PM aggregator (same pattern as the fleet feed / quota panel).

4. Gap-closers identified by the first pass

  • Porkbun API key is domain-restricted: dns/retrieve succeeds only for security.cab and refuses the other 38 domains, so zone-file-grade exports don't exist yet for the Porkbun-hosted zones (first pass used a public-DNS probe instead). Extend the key's domain allowlist in the Porkbun control panel, or file an unrestricted key in Bitwarden (by name) and point the job at it.
  • Second Porkbun account: drewrypope.com (expires 2028-06-14) and boats.dev (expires 2027-04-02) are Porkbun-registered but absent from the developingtoday API account — identify the account and bring both domains under the same inventory (auto-renew state and full exports are currently unknowable).
  • security.cab expiry discrepancy: Porkbun account says expires 2027-02-24 (created 2018-02-24); registry RDAP said 2027-08-14. Resolve against the Porkbun control panel and correct whichever source is wrong.

Acceptance

  • Backup job runs on schedule and produces a diffed snapshot without human prompting.
  • Register history shows ≥2 cycles of renewal/price observations appended automatically.
  • Aggregator renders the three lists from the structured export.
  • A test domain entering the renewal window produces the spend alert.

Acceptance criteria

  • A scheduled (weekly or better) read-only job exports the full DNS config for every owned zone/domain (Cloudflare + Porkbun) into a dated snapshot in the seed snapshot's format, using the Bitwarden-stored API credentials.
  • Each snapshot is diffed against the previous one; record changes, new/removed domains, expiry-date changes, and price changes are surfaced in the run output.
  • A renewal/cost history accumulates per domain (observation date, renewal date, list renewal price; actual charged amounts when an invoice-grade source is reachable).
  • An alert fires when a domain enters the renewal window (≤45 days) or a renewal price changes cycle-over-cycle.
  • A dashboard view shows the domain register sorted by next renewal, flagging unused and earmarked-but-unused domains.

Activity

  1. developing-today commented on Oct 25, 2026

    @developing-today
    Contributor
  2. dezren39 commented on Oct 25, 2026

    @dezren39
    MemberAuthor

    Correction to the gap-closers list, same day as filing: item (c), the "security.cab expiry discrepancy", does not exist. The first pass briefly worked from a partial RDAP read (2027-08-14); the full registry RDAP record — re-fetched and saved in the seed snapshot under rdap/security.cab.json — agrees with the Porkbun account exactly: registered 2018-02-24, expires 2027-02-24, transfer recorded 2024-02-15. RDAP for developing-today.com and developing.today likewise matches the account dates. Gap (c) is closed; the register and snapshot README have been corrected. Items (a) the domain-restricted Porkbun API key and (b) the unidentified second Porkbun account (drewrypope.com, boats.dev) remain open as filed.

  3. dezren39 commented on Oct 25, 2026

    @dezren39
    MemberAuthor

    Updates from Drew (2026-10-25), corrections applied to the register (commit 9b44e22 in hatch-personal):

    1. boats.dev is NOT Drew-owned — it is a third-party container-hosting service. Removed from the register; portfolio is now 40 domains. Do not include it in backups or renewal tracking.
    2. drewrypope.com + a couple of other domains are in a second Porkbun account that IS Drew's. Its API access is now a tracked to-do for Drew with weekly nudges (stop phrase "porkbun done"); when it lands, this issue's gap 2 closes.
    3. DNS spending review scheduled: calendar event + chat reminder, Monday 2027-01-11 — the keep/lapse decisions from that review will feed this issue's renewal tracking.
    4. The dashboard view of this register is owned by impl(dashboard): slice 3 — pluggable pages (DNS register first), comments everywhere, agent/session identity registry #906 (slice 3: pluggable pages, DNS page first). This issue owns the data pipeline (backup job + register format); impl(dashboard): slice 3 — pluggable pages (DNS register first), comments everywhere, agent/session identity registry #906 renders it. Keep the register format stable and document any format change on both issues.
  4. added a commit that references this issue on Oct 25, 2026
  5. dezren39 commented on Oct 25, 2026

    @dezren39
    MemberAuthor

    Coordination note from #906 (dashboard DNS page, now live).

    The dashboard's /dns page (PR #920) renders the register through the fleet-feed collector, which parses the register Markdown keyed on its exact table header row:

    | Domain | Registrar | DNS hosted at | Next renewal | Auto-renew | Renewal price (list) | Prior-year cost | Where it's used | Plan / status | Review? |

    Two things for this issue's recurring pipeline:

    1. Keep that header row stable. If the register format changes (columns renamed/reordered), update src/dns.ts (parseDnsRegister) in the aggregator in the same pass — the parser fails soft (no section, page says why) rather than rendering garbage, so a silent format break shows up as the DNS page going absent, not wrong.
    2. Status + total semantics. The page's in-use / unused-but-earmarked / completely-unused split is derived from the Plan/status cell: starts with "Active" → in use; contains "earmark" → earmarked; anything else → unused. That reproduces the register's current 3 / 18 / 19 — new rows should keep those wordings if they want the same buckets. The dashboard total ($606.70/yr) is the sum over all 40 rows; the register's $595.62 headline counts the main Porkbun account's 39 (drewrypope.com, $11.08, is the second account). Worth aligning the register's headline wording when this pipeline next touches it, so the two numbers stop looking like a discrepancy.
  6. added
    kind/featureNew feature or capability request
    source/humanFiled directly by a human
    priority/normalOrdinary priority (the default)
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    area/infraInfrastructure, deployment, and operations
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 26, 2026
  7. dezren39 commented on Oct 26, 2026

    @dezren39
    MemberAuthor

    Intake review

    Fleet monitor intake pass (2026-10-25 run). Acceptance criteria appended to the body this pass (original body preserved verbatim above it).

    State per the body: The first pass is DONE (register of 41 domains + seed snapshot, 2026-10-25); this issue covers only the recurring machinery that pass deliberately did not build — the scheduled backup job, renewal/cost history, and the dashboard view. Nothing in this repo or the register suggests any of the three recurring pieces exist yet.

    Related: #910 (org-level GitHub Project / dashboard layer — a different dashboard surface, but the DNS dashboard view should follow the same fleet-feed collector pattern rather than inventing a second one).

    Notes / suggested approach: Build in the body's own order: (1) the read-only scheduled export + diff is the foundation and is independently useful; (2) the history append falls out of the snapshot format almost for free once (1) exists — decide the history file's home (beside the register) in that step; (3) the dashboard view last, reading the register + history. Credentials are Bitwarden-stored per the body; the job must stay read-only against Cloudflare/Porkbun.

    Risks: Porkbun/Cloudflare API shapes changing under the job (pin expectations, fail loud on schema drift); renewal-window alerts becoming noise if the window is too wide — 45 days with universal auto-renew is about spend visibility, as the body notes. Effort classed l (three-part build).

  8. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note — triage verdict: CONFIRMED, still unbuilt; slice 1 routed (queue task 2284, lane D, 2026-11-01T02:4xZ)

    Verified against live state today:

    • Item 1 (scheduled backup job) — NOT BUILT. The lab-cluster goal's hidden_files/ contains only the seed dns-backup-2026-10-25/; no later snapshot exists. The cron list has no DNS backup job (only the one-shot dns-spending-review-reminder for 2027-01-11 and the porkbun-account-nudge).
    • Item 2 (renewal/cost history) — NOT BUILT. No history file beside the register; zero automated observation cycles so far.
    • Item 3 (dashboard view) — DONE in first form. Per the impl(dashboard): slice 3 — pluggable pages (DNS register first), comments everywhere, agent/session identity registry #906 coordination note, the aggregator /dns page is live via PR impl(dashboard): slice 3 — pluggable pages, DNS register, comments, session registry (#906) #920, rendering the register's three lists — but it parses the Markdown register on its exact table header, so the structured register.json export is still owed and is folded into slice 1 below.
    • Item 4 (gap-closers) — unchanged: Porkbun key allowlist extension and the security.cab expiry resolution are control-panel actions; the second-account identification is owned by the second-porkbun-account… goal + its weekly nudge cron.

    Routing: follow-up queue task 4867 (small_fix) enqueued — slice 1: the weekly read-only backup job (dated snapshot in the seed format, diff vs previous, renewal/price history appends, register.json export, ≤45-day renewal-window spend alert). This issue stays open as the tracker for the remaining acceptance (≥2 history cycles maturing, gap items).

  9. 37 remaining items

  10. added
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  11. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    state: triaged (coverage pass)
    Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 40 (priority/normal). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
    Found via: coverage pass 2026-11-03

  12. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  13. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: picked_up
    task: 7602 (repo-health-code-pilot, repo-worker-code — OpenChamber scheduled lane c29174f1)
    state: Claimed via atomic queue claim. Revalidating issue #902 (triage, timebox 20m).
    next: Confirm which parts of the issue are still live at current state, then scope remaining work or close with evidence.

  14. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: triaged
    task: 7602 (repo-health-code-pilot, repo-worker-code — OpenChamber scheduled lane c29174f1)
    state: Triage verdict CONFIRMED LIVE, not closeable yet. Checked at main cd3b299ae (2026-11-03).

    Per-acceptance state:

    1. Scheduled backup job — BUILT, schedule not yet observed. dns-backup.py (hatch-personal goal stand-up-lab-cluster-talm-talos-cozystack-postgres-rustfs/hidden_files/) has two produced snapshots: dns-backup-2026-11-01 and dns-backup-2026-11-01-b, each with Cloudflare + Porkbun exports, README.md, diff-vs-previous and <=45d alert sections. --self-test passes today (fixture inside 45d named, outside not; diff detects add/expiry/price; identical snapshots diff empty). The weekly job dns-backup-weekly was reported created/enabled on 2026-11-01 with its first scheduled run Sun 2026-11-08; no scheduled-run evidence exists yet because the date has not arrived.
    2. Renewal/cost history — mechanism BUILT, cycle count short. files/dns-renewal-history.md has 2 appended blocks, but both are observation date 2026-11-01 (initial run + immediate re-run), total list renewal $612.54/yr across 40 domains. The "≥2 cycles" acceptance needs the 2026-11-08 and 2026-11-15 runs to mature.
    3. Dashboard from structured export — DONE. files/register.json is valid (40 domains); pm-stack/aggregator/src/dns.ts (+ test/slice3.test.ts) consumes it with a visible Markdown fallback (PR feat(aggregator): /dns consumes register.json with Markdown fallback (#902) #4366, merge d21c18790), so the three lists render from JSON.
    4. Gap-closers — unchanged, blocked outside this repo. Porkbun key domain allowlist extension is a control-panel action (Drew); the second Porkbun account (drewrypope.com) is owned by the second-porkbun-account… goal + weekly nudge cron; the security.cab expiry discrepancy was closed 2026-10-25 (RDAP agrees with Porkbun: expires 2027-02-24).

    Routing: issue stays OPEN. Follow-up queue task 8054 (not-before 2026-11-09T15:00Z) will verify the first scheduled run landed, that history gained a second distinct cycle, and then close #902 with evidence or record the exact residual gap. No competing PR or branch exists for this issue.

  15. added
    status/triagedTriaged and routed (see agent note)
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 3, 2026
  16. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    event: done
    task: 7602 (repo-health-code-pilot, repo-worker-code — OpenChamber scheduled lane c29174f1)
    state: Completed as triaged; issue #902 stays OPEN by design.
    fresh_evidence: Triage comment of 2026-11-03 (this run) records the per-acceptance state at main cd3b299ae: slices 1-2 built (weekly job + snapshots + history + register.json + aggregator JSON consumption, PR #4366 merged), remaining acceptance date-gated on the 2026-11-08/2026-11-15 runs and on Drew's Porkbun control-panel actions.
    next: Follow-up queue task 8054 (not-before 2026-11-09T15:00Z) verifies the first scheduled run, the second distinct history cycle, and closes #902 or records the residual gap. Queue labels for task 7602 cleared; status/triaged set.
    bookkeeping: Sandbox backlog and sweep log deferred to fleet reconciliation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codearea/infraInfrastructure, deployment, and operationseffort/lLarge: multi-day or open-ended; expect staged check-inskind/featureNew feature or capability requestmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedmode/onlineFully doable remotely: design, config, code, cloudpriority/normalOrdinary priority (the default)source/humanFiled directly by a humanstatus/triagedTriaged and routed (see agent note)task/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions