Skip to content
Merged
2 changes: 1 addition & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file collects architecture and design notes for studio that aren't otherwis

- [`src/features/clusters/DESIGN.md`](src/features/clusters/DESIGN.md) — cluster list lifecycle classification, optional metadata and notification scope, and when the cluster form may terminate a cluster.

- [`src/features/cluster/DESIGN.md`](src/features/cluster/DESIGN.md) — when the Starting-up and Scaling screens may report done, how clone progress is read, and where the cluster card's sync chip gets its data.
- [`src/features/cluster/DESIGN.md`](src/features/cluster/DESIGN.md) — when the Starting-up and Scaling screens may report done, how clone progress is read, where the cluster card's sync chip gets its data, how custom-domain cert intent, certificate progress and the Bind selection work, and why a domain change is built from a fresh read.

- [`src/features/instance/databases/DESIGN.md`](src/features/instance/databases/DESIGN.md) — when the browse grid may show its spinner, why a table without a primary key can't be listed, why a filtered list pages without a count, why a table with a computed attribute exports by naming its columns, where columns for undeclared properties come from, and why a filter no index can answer asks first.

Expand Down
45 changes: 45 additions & 0 deletions src/features/cluster/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,48 @@ page for anything else. Handle it there, not per page: every page under the rout
The cluster card links a `PROVISIONING` cluster to Starting-up and an `UPDATING` one to Scaling for every viewer, so a
non-admin on a new cluster lands on that message instead of a dead card
([HarperFast/studio#1773](https://github.com/HarperFast/studio/issues/1773)).

## Custom domains: cert intent is cluster-wide, and certificate progress is polled

`generateDomainCerts` is one flag for the whole cluster. Central manager reads an absent flag on a `domainIds` edit as
unchanged, and stamps the result onto every running instance and the Cluster row, which later expansions read
([HarperFast/central-manager#669](https://github.com/HarperFast/central-manager/pull/669)). So only a bind states it:
an unbind that sent `false` would revoke it for every domain still bound
([HarperFast/studio#1626](https://github.com/HarperFast/studio/issues/1626)).

Nothing pushes certificate progress. Central manager answers the domain change before host manager acts on it; host
manager then upserts a `ChallengeCertificate` row per added domain, and Studio reads progress from that row's
`issueDate` and `inProgress`. The Domains table polls those rows at 5s while one is generating or for three minutes
after a domain change (`watchChallengeCertificates`, which the mutation calls), and once a minute otherwise. A cluster
without the table answers 404, which `getSearchByValue` reads as an empty result, so an idle poll never errors and
nothing else stops it ([HarperFast/studio#1630](https://github.com/HarperFast/studio/issues/1630)).

The Bind selection is page state that outlives its rows, so Bind counts and sends only the ticked domains the current
list still offers for binding (`canBindDomain`, the same predicate that renders the checkbox). A removal takes its row
out of the cached list as soon as it succeeds, rather than when the refetch lands, and Bind waits while one is in flight
([HarperFast/studio#1800](https://github.com/HarperFast/studio/issues/1800)).

## Custom domains: a domain change is built and checked from a fresh read

`PUT /Cluster/{id}` replaces the cluster's whole `domainIds` list, and central manager unbinds every domain the new
list leaves out without checking which cluster that domain is bound to. So callers of `useSetDomainIdsOnCluster` pass
intent (`add`, `remove`), and the mutation builds the list from a fresh read of the cluster and of the organization's
domains, never from what the page last saw: another tab or member may have bound a domain since.

Before sending, it refuses what central manager would refuse, so the user gets a reason instead of a bare 400
([HarperFast/studio#1686](https://github.com/HarperFast/studio/issues/1686)), in
[`describeDomainChangeRefusal.ts`](../clusters/mutations/describeDomainChangeRefusal.ts):

- **The cluster can't change.** Central manager's `assertClusterMutable` refuses every cluster update, edits included,
unless the cluster is `RUNNING` and no instance is `FAILED` or in its `TRANSITIONING_INSTANCE_STATES`, which include
`STOPPED` and `UPDATED`. Studio keeps a copy of that list. A status missing from the copy still reaches central
manager, whose own refusal is then shown, so the copy must never list more than central manager's does.
- **A domain it touches is refused.** That covers a missing, unverified or foreign-bound domain, and a self-managed
cluster. Studio refuses rather than drops. Dropping a domain bound elsewhere would unbind it there. Dropping a deleted
id doesn't help either: Harper resolves a missing id in the `domains` relationship to `undefined`, so central
manager's own removal loop fails on such a cluster whatever the request carries. A refusal over a domain the cluster
already lists points at support, since nothing on the page clears it.

Failures stay on the page beside the control, for both domain changes and cluster edits, and the global toast is off
for both mutations. Each mutation reports a real request failure to RUM itself, at mutation level, but not a refusal,
which is Studio declining to send.
Loading
Loading