fix: post a registry snapshot to the dashboard after each sync - #173
Open
bosquejun wants to merge 3 commits into
Open
fix: post a registry snapshot to the dashboard after each sync#173bosquejun wants to merge 3 commits into
bosquejun wants to merge 3 commits into
Conversation
The dashboard lists domains from a D1 table that is a read model of this repo, refreshed only by a full-snapshot POST to /api/registry/events. That POST was never implemented here β it existed only as an example snippet in the ecosystem repo's apps/dashboard/README.md β so the table has been serving whatever a one-off backfill left in it. Both halves of the symptom follow from that. example.is-pinoy.dev was deleted from the registry on 2026-08-09 and is still listed; bosquejun.is-pinoy.dev was registered eight hours later the same day and has never appeared. The fallback to the GitHub API never engages, because D1 is reachable and simply stale. The sync job now builds the snapshot with scripts/build-snapshot.sh and posts it after the Cloudflare sync. Details that are load-bearing: - `fetch-depth: 0`, since each record is dated from the first and last commit to touch its file and a shallow clone has no history to read. - The POST runs under `if: always()` and reports `status: failed` when the sync failed, so a bad run marks the rows instead of leaving the read model untouched. The job is failed again afterwards so the run still goes red. - It fails loudly when REGISTRY_SYNC_SECRET is unset or the endpoint rejects the payload. Silence is what caused this. - Destroyed entries are omitted, so reconcile deletes their rows. Also restores subdomains/example.json with `"destroy": true`. Deleting the file in #150 never removed the DNS record β sync only deletes what is still present and marked for destruction β so the CNAME has stayed live in Cloudflare this whole time. The next sync will remove it; the file can be deleted after that. Requires REGISTRY_SYNC_SECRET as a repository secret, matching the value set on the dashboard deployment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3VaS59vjtX2XdfhK28z7Y
β Subdomain Validation PassedAll subdomain files are valid.
|
8 tasks
Restoring subdomains/example.json with "destroy": true was meant to make sync delete a CNAME that deleting the file in #150 had left behind. The record is not there. The validation dry-run on this PR settles it: scoped to the one changed domain with example marked for destruction, against 71 records fetched from Cloudflare, it reported "No changes needed." The DELETE branch for a destroyed domain is not gated by --only scoping, so a record at that name would have produced one. example.is-pinoy.dev also fails to resolve, while bosquejun.is-pinoy.dev returns Cloudflare proxy addresses. So the record was removed by some other route after #150, and the tombstone would sit in the registry forever describing a subdomain that neither exists nor is claimed. Dropping it costs nothing here: the stale dashboard row is removed because the snapshot enumerates the files that exist, and a file that is simply gone is absent from it either way. The underlying gap β that deleting a domain file emits no DELETE at all β is real regardless, and is handled in is-pinoy-dev/ecosystem#226. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3VaS59vjtX2XdfhK28z7Y
The workflow only fired on pushes touching subdomains/**, which was fine when its only job was reconciling DNS from those files. Now that it also refreshes the dashboard's read model, that filter has two gaps. Merging a change to the sync mechanism itself does not run it, so this PR would land without ever posting a snapshot and the dashboard would stay stale until the next registration happened to arrive. And there was no way to re-run a sync on demand after a failed run or a drifted read model. Adds workflow_dispatch, and puts the workflow and the snapshot script in the path filter so a change to either re-runs it. Sync is idempotent β it diffs against Cloudflare and does nothing when there is nothing to do β so the extra runs are cheap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3VaS59vjtX2XdfhK28z7Y
bosquejun
added a commit
to is-pinoy-dev/ecosystem
that referenced
this pull request
Aug 24, 2026
The changeset said the record "kept answering for weeks" after its file was deleted. It did not. The validation dry-run on is-pinoy-dev/domains#173 β scoped to that domain with it marked for destruction, against 71 records fetched from Cloudflare β reported no changes needed, and the name does not resolve. It was removed by some other route after the file was deleted. The gap this changeset describes is unaffected: deleting a domain file still emits no DELETE, and the run still prints "All domains are in sync" either way. That indistinguishability is the point, and is what the warning fixes β so the claim is restated as what the output does not tell you, rather than as a record that was left live. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3VaS59vjtX2XdfhK28z7Y
Member
Author
|
WIP |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's wrong
The dashboard lists domains from a D1 table that is a read model of this repo, refreshed only by a full-snapshot
POST /api/registry/events. That POST was never implemented here β it existed only as an example snippet in the ecosystem repo'sapps/dashboard/README.md. Neither repo referencedregistry/eventsorREGISTRY_SYNC_SECRETanywhere. So the table has been serving whatever a one-off backfill left in it.Both halves of the reported symptom follow from that one gap:
example.jsondeleted (#150)bosquejun.jsonadded (#154)The snapshot predates both, so
exampleis still a row and renders on the dashboard, andbosquejunwas never inserted and doesn't. The fallback to the GitHub API never engages, because D1 is reachable and simply stale.The fix
The sync job now builds the snapshot with
scripts/build-snapshot.shand posts it after the Cloudflare sync. Load-bearing details:fetch-depth: 0on checkout β each record is dated from the first and last commit to touch its file, and a shallow clone has no history to read.if: always()and reportsstatus: failedwhen the sync failed, so a bad run marks the rows rather than leaving the read model untouched. The job is failed again afterwards so the run still goes red.REGISTRY_SYNC_SECRETis unset or the endpoint rejects the payload. Silence is precisely what caused this.destroy: trueentries are omitted, and a file that is simply gone is absent too β which is what removes the staleexamplerow.Two details of the date command differ from the ecosystem README's original recipe, because the real data breaks it: no
--follow(git scores a copied-from-a-neighbour file as a rename, datingbosquejun.jsonfrommee.jsonβ before the subdomain existed), and UTC with a literalZ(z.iso.datetime()rejects the numeric offset%cIproduces, which would 400 the whole payload).Verified by running the real
reconcile()against the real generated snapshot: it passes the endpoint's own Zod schema, deletesexample, and insertsbosquejunwithcreatedAt2026-08-09T12:53:49Z.A correction, mid-PR
This PR originally also restored
subdomains/example.jsonwith"destroy": true, on the theory that deleting the file in #150 had left its CNAME live in Cloudflare. That was wrong, and this PR's own CI caught it.The validation dry-run on the first commit β scoped to the one changed domain, with
examplemarked for destruction, against 71 records fetched from Cloudflare β reportedNo changes needed.The DELETE branch for a destroyed domain isn't gated by--onlyscoping, so a record at that name would have produced one.example.is-pinoy.devalso fails to resolve, whilebosquejun.is-pinoy.devreturns Cloudflare proxy addresses.The record was removed by some other route after #150. The tombstone is dropped in 5f4434d β it would have sat in the registry forever describing a subdomain that neither exists nor is claimed, and the stale dashboard row is removed either way.
The underlying gap it was meant to expose β that deleting a domain file emits no DELETE at all β is real regardless, and is handled in is-pinoy-dev/ecosystem#226.
Set
REGISTRY_SYNC_SECRETas a repository secret, matching the value on the dashboard deployment. The sync job uses repo-level secrets, so it goes there rather than theproductionenvironment. Without it, every sync run fails after this merges β deliberate, but immediate.Optionally set a
DASHBOARD_URLrepository variable; it defaults tohttps://dashboard.is-pinoy.dev.Merging no longer self-triggers: the net diff no longer touches
subdomains/**, and the workflow'spaths:filter only fires on those. Either wait for the next registration, or run the sync workflow manually to refresh the dashboard immediately.π€ Generated with Claude Code
https://claude.ai/code/session_01S3VaS59vjtX2XdfhK28z7Y