CluPilotCloud/app/Models
nexxo ea643b5e73
tests / pest (push) Failing after 8m17s Details
tests / assets (push) Successful in 23s Details
tests / release (push) Has been skipped Details
Prove a custom domain before serving it, and keep proving it
Two things.

── The update screen, still opening twice ───────────────────────────────────
Reported again on 1.3.9, and the cause was not the one fixed in 1.3.8. The
agent consumes the request file BEFORE it resolves the release — deliberately,
because update.sh may kill the shell and a request left in place would loop —
and writes `state: running` only once it has decided to go ahead. In between,
the request is gone and the status does not say running yet, so the endpoint
honestly answers "nothing is running". The watcher read that as "the run has
finished" and reloaded the page: overlay on the click, gone a poll later, 503
after it.

The overlay now closes only once the server has BOTH confirmed a run and then
stopped reporting it. Before the confirmation, silence means the agent has not
got there yet. Bounded at twenty polls so a request the agent refuses does not
leave the console covered forever.

── Custom domains, proven and re-proven ─────────────────────────────────────
`custom_domain` was a free-text field and everything downstream believed it:
the proxy served it, the certificate was issued for it, Nextcloud trusted it.
Anyone who pointed any hostname at the platform got somebody else's files
under their own name.

The proof is a TXT record at _clupilot-challenge.<domain> holding a token only
this instance has. Nothing is served until it has been read. Every reader now
goes through Instance::address(), which is the one place the decision is
made — `custom_domain ?: subdomain` was the hole, written out four times.

It is re-read every night at 03:40, because a token checked once can be taken
straight back out and a domain that later lapses keeps resolving here. Three
consecutive misses before a live domain is withdrawn: one failed lookup is a
nameserver having a bad minute, and withdrawing takes a working Nextcloud off
its own address. The domain and its token stay on the row so the customer can
put the record back rather than start over.

Changing the domain mints a NEW token. Reusing it would let somebody who once
verified example.com claim any other domain later without touching its DNS —
the old record is still sitting there and only the value is compared.

The domain is changeable at any time, and removable. Fixing it once set was
considered and rejected: adding or moving an address is a proxy entry, a
certificate and one line in trusted_domains.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:47:51 +02:00
..
Builders feat(billing): a proof register, and modules frozen at their booked price 2026-07-26 13:07:34 +02:00
Concerns feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
Backup.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
Customer.php Check users directly for the reverse operator-identity collision, not just customers 2026-07-28 15:36:29 +02:00
Datacenter.php Let a datacenter code be corrected while nothing depends on it, and say which building 2026-07-28 22:25:21 +02:00
DnsRecord.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
ExportTarget.php Let a destination say how it is laid out and how long anything stays 2026-07-29 03:08:27 +02:00
Host.php Read VPN host state from the synced peer table, not a live hub call 2026-07-29 00:18:21 +02:00
Incident.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
IncidentUpdate.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
Instance.php Prove a custom domain before serving it, and keep proving it 2026-07-29 14:47:51 +02:00
InstanceMetric.php Measure availability, and let a customer move down again 2026-07-27 16:41:15 +02:00
InstanceTraffic.php feat(traffic): meter the monthly allowance, show it, throttle instead of blocking 2026-07-25 23:33:47 +02:00
Invoice.php Let the export have as many destinations as somebody wants 2026-07-29 02:43:15 +02:00
InvoiceExport.php Let the export have as many destinations as somebody wants 2026-07-29 02:43:15 +02:00
InvoiceSeries.php Lay the foundation for self-issued invoices: series, numbers, frozen documents 2026-07-29 00:57:06 +02:00
LoginSession.php Recognise the devices an account signs in from, and warn about a new one 2026-07-28 23:28:34 +02:00
Mailbox.php Clear every mailbox's verification when the shared server config changes 2026-07-28 06:36:06 +02:00
MaintenanceNotification.php fix(admin): in-flight claim (claimed_at) for exactly-once maintenance send; scope permission rollback 2026-07-25 16:38:44 +02:00
MaintenanceWindow.php fix(portal): scope the per-instance maintenance badge to that instance's host 2026-07-25 19:12:33 +02:00
MonitoringTarget.php Move the console off /admin, give the status page its own address, and measure monitoring 2026-07-27 06:05:40 +02:00
OnboardingTask.php feat(engine-b): customer domain models + migrations 2026-07-25 11:38:01 +02:00
Operator.php Give the people who run CluPilot a table of their own 2026-07-28 09:20:54 +02:00
Order.php Let a subscription or add-on be opened for free, with who and why on the row 2026-07-29 12:41:06 +02:00
PlanFamily.php Let the owner mark one plan as recommended, from the console 2026-07-29 14:22:22 +02:00
PlanPrice.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
PlanVersion.php feat(billing): the plan catalogue becomes three tables, and config stops selling 2026-07-26 12:05:56 +02:00
ProvisioningRun.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
ProvisioningStepEvent.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
RunResource.php feat(engine): core provisioning data model + hosts 2026-07-25 09:49:43 +02:00
Seat.php feat(portal): settings page — company profile, branding (logo+colors), cancel package, close account 2026-07-25 14:36:52 +02:00
StatusDay.php Rebuild the status page as a status page 2026-07-29 12:45:18 +02:00
StripePendingEvent.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
Subscription.php Let a subscription or add-on be opened for free, with who and why on the row 2026-07-29 12:41:06 +02:00
SubscriptionAddon.php Let a subscription or add-on be opened for free, with who and why on the row 2026-07-29 12:41:06 +02:00
SubscriptionRecord.php feat(billing): Stripe owns the billing cycle, we own capability 2026-07-26 13:36:28 +02:00
SupportRequest.php Editing in modals, an update button that is not gated on a stale reading, and a support page that is real 2026-07-27 17:55:49 +02:00
User.php Require a confirmed address before an account can use anything 2026-07-28 23:43:20 +02:00
UserDevice.php Repair two comment blocks the admin-hosts edit ran together 2026-07-28 23:20:43 +02:00
VpnPeer.php Move the console's identity out of the customer table 2026-07-28 10:31:43 +02:00