Skip to content

console: let self-managed instances set a display name - #38691

Open
djahandarie wants to merge 1 commit into
MaterializeInc:mainfrom
djahandarie:console-instance-appearance
Open

console: let self-managed instances set a display name#38691
djahandarie wants to merge 1 commit into
MaterializeInc:mainfrom
djahandarie:console-instance-appearance

Conversation

@djahandarie

@djahandarie djahandarie commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

What

Adds an optional spec.consoleAppearance to the Materialize CRD so that each
self-managed instance's console can name itself:

apiVersion: materialize.cloud/v1
kind: Materialize
spec:
  consoleAppearance:
    displayName: prod

The console appends the name to its browser tab title, e.g. Materialize Console · prod. The field is optional, and has no effect in Materialize Cloud,
which serves a single console for all of an organization's regions.

Recoloring the console's accent was originally part of this change and has
moved to #38720 per review, so that it can be discussed on its own.

Why

Every console looks identical, so an operator running more than one Materialize
instance cannot tell from a browser tab which instance a console is pointed at.
The only distinguishing signal today is the URL, which is easy to miss, so a
change intended for a dev instance is one tab away from landing on production.
The workarounds are per-instance browser profiles or a patched console image,
and neither survives an upgrade.

How

The console already fetches an app-config.json that orchestratord renders
from the Console resource, so this reuses that channel instead of adding one:

Materialize.spec.consoleAppearanceConsole.spec.appearance
app-config.jsonappConfig.appearance in the console.

The title is set from that config before the console authenticates, so the name
is on the login screen too.

consoleAppearance is a struct rather than a flat consoleDisplayName so that
further per-instance appearance settings can join it without another top-level
field.

consoleAppearance is excluded from the rollout hash and is skipped during
serialization when unset. Setting or changing it therefore reconciles only the
console's ConfigMap, which the console picks up on its next page load with no
pod restart, and adopting an operator version that knows the field does not
roll instances that leave it unset.

The CRD field reference under doc/user/data/self_managed/ is regenerated with
the crd-writer step that bin/bump-version runs.

Tests

  • console_appearance_does_not_affect_the_rollout_hash in mz-cloud-resources
    covers both rollout-hash properties above.
  • A ConsoleAppearance modification in test/orchestratord/mzcompose.py
    covers the passthrough from the Materialize CR to the console's
    app-config.json.
  • Console unit tests cover the tab title and the app-config parsing.

The subjective part: where this configuration belongs

The field sits on the Materialize CR and is exempt from the rollout hash. That
is the choice in this change I would most like a second opinion on, so here is
the reasoning and what it costs.

Why the CR. It is where every other per-instance console setting already
lives (consoleReplicas, consoleResourceRequirements,
consoleExternalCertificateSpec), so it is where an operator already looks.
It is declarative and versioned alongside the rest of the instance, and it
reaches the browser through the app-config.json the operator already writes,
so no new delivery mechanism has to be built or watched.

What that costs. A field on a versioned CRD is close to permanent: it has
to be carried through both CRD versions and the conversion webhook, which is
the bulk of the Rust in this diff, and withdrawing it later is a breaking API
change. It also puts presentation into an API whose every other field describes
infrastructure, which invites the follow-on requests (a logo, a favicon,
arbitrary CSS) that this deliberately does not open the door to.

The rollout-hash exemption is a smaller but real burden. Exclusion is enforced
by a hand-maintained struct literal in generate_rollout_hash plus a
skip_serializing_if, neither of which is visible from the field's type, and
getting it wrong means a cosmetic edit rolls production. Exempting it is not
optional though: a retitle that costs an environmentd rollout is a feature
nobody would use. The precedent is already set by consoleReplicas and both
services' resource requirements, so this follows an existing pattern rather
than inventing an exemption. If the pattern deserves a stronger mechanism than
prose plus a struct literal, that is worth its own change.

Alternatives, and why I did not take them.

  • The system parameter ConfigMap (systemParameterConfigmapName). Wrong
    consumer, and too late in the boot sequence. Those are database settings that
    environmentd applies and SHOW ALL reports, and nothing in environmentd
    needs to know what the console calls itself. More decisively, the console
    fetches app-config.json before it authenticates, so the name is on the
    login screen. Anything sourced over SQL cannot be, because it would need a
    session first.
  • A ConfigMap of its own, watched by the operator. This is the credible
    alternative if we would rather keep the CRD strictly about infrastructure.
    It keeps cosmetics out of a permanent API and cannot touch a
    rollout-relevant field by construction. It costs a second object to create
    and own, RBAC and orphan handling, no schema validation, and drift from the
    manifest that describes the rest of the instance. It is more machinery than
    a CRD field, not less.
  • A setting in the console UI, kept in the browser. This is what the existing
    light and dark switcher does. It is per browser, so it protects whoever sets
    it and nobody else, which is the exact failure this feature exists to
    prevent. Worth having as a complement, wrong as the primary.
  • A setting in the console UI, persisted server side. The console is static
    files behind nginx with no server of its own, so persistence would mean the
    catalog, which lands back on the first bullet and adds a write path and
    privileges to design.
  • An env var on the console Deployment, or an image per environment. Both are
    the workarounds this replaces. The operator owns the Deployment and
    overwrites the env var, and a patched image has to be rebuilt on every
    upgrade.
  • Helm values on the operator. Wrong scope: one operator manages many
    instances, and this is per instance.

Notes for reviewers

  • displayName could default to metadata.name rather than being configured
    at all, which would give every instance a useful tab title with no
    configuration. I kept it explicit because that name is often neither short
    nor human: the install docs name the CR
    12345678-1234-1234-1234-123456789012. But if we would rather have the
    zero-config version, this feature needs no new field at all.

🤖 Generated with Claude Code

@djahandarie
djahandarie marked this pull request as ready for review September 7, 2026 04:48
@djahandarie
djahandarie requested review from a team as code owners September 7, 2026 04:48
@djahandarie

Copy link
Copy Markdown
Contributor Author
1-default-light 2-red-light 3-green-light 4-red-dark 5-red-shell

@Alphadelta14 Alphadelta14 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd feel most comfortable if we made the displayName (okay with) and accents as discrete changes (need coordinated direction on this one)

Comment thread console/src/theme/accent.ts Outdated
* is the default, and names the shades the rest of the palette was designed
* around.
*/
const ACCENT_SHADES: Record<

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels like this is built from scratch more than taken from our theme/colors.ts.

And if we were to be looking at alternative skinning, I'd normally recommend using a slightly more even palette.

Like a single hue sequential
https://colorbrewer2.org/#type=sequential&scheme=Purples&n=9
or a multi-hue sequential
https://colorbrewer2.org/#type=sequential&scheme=BuPu&n=9

Or for a Paired set
https://colorbrewer2.org/#type=qualitative&scheme=Paired&n=12
(You'd remove the ones that don't make sense)

but we'd probably need proper design input on this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to #38720 along with the rest of the accent work, so this can be settled there.

You're right that it's built rather than taken. The table exists because theme/colors.ts wasn't drawn as a set of interchangeable accents: the hues don't run parallel, so reading purple's positions for every hue puts green[500] link text at 3.3:1 on white and paints the primary button in red[600], which is a muted brick next to that ramp's crimson. Picking shades per hue was the smallest way to keep contrast honest, not a claim that it's the right palette.

An even scale designed for the purpose is the better fix, and it would collapse the table back to a single rule and give more than three hues to choose from. I've noted the colorbrewer sequential/qualitative sets in #38720 and left a TODO at the table pointing at the same question, so it's blocked on design input rather than merged on my judgment.

Comment thread src/cloud-resources/src/crd.rs Outdated
/// appends it to its browser tab title.
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
/// The hue the console accents its interface with: `blue`, `green`,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like having all of the distinct values in our comment. but I guess we don't show our enum values in our docs... 😒

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the underlying cause instead, in #38720: crd-writer only listed an enum's values when its variants carried doc comments, because schemars describes those with oneOf and a bare enum with a plain array of values. It now reads both, so the field documents its own values and the prose list is gone.

Side effect worth knowing about: privateKeyAlgorithm picks up the RSA/ECDSA/Ed25519 list it was also missing, so that entry in the generated reference changes too.

This PR no longer has an enum in it at all, so the comment you were looking at is gone from here either way.

Comment thread console/src/theme/accent.ts Outdated
> = {
purple: { light: { primary: 600, bright: 500 }, dark: { primary: 400 } },
// Red is muted rather than vivid at 600, so it accents a shade lighter.
red: { light: { primary: 500, bright: 500 }, dark: { primary: 400 } },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accents should exclude red and green options to not be too conflicting with errors in the console (hard to find errors on a red page).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #38720: red and green are no longer offered, leaving blue, orange, and purple (purple so an operator can pin the default explicitly). The enum's doc comment records why they're absent, so it doesn't get "helpfully" re-added later.

Worth flagging the cost, since red for production is the case operators reach for first: with three hues, one of which is the default, there are two real choices for telling instances apart. That's another reason the palette question in the other thread matters, since a wider even scale would give non-conflicting hues to spend.

Consoles are otherwise identical, so an operator running several
Materialize instances cannot tell from a browser tab which instance a
console is pointed at, and a change meant for dev is one tab away from
landing on prod.

Adds `spec.consoleAppearance` to the Materialize CRD:

    spec:
      consoleAppearance:
        displayName: prod

Orchestratord copies it onto the Console resource and into the
`app-config.json` it already writes for the console, so no new
configuration channel and no console rollout are needed to pick it up.
The console appends the name to its browser tab title. It sets the title
from the app config before authenticating, so the name is there on the
login screen too.

`consoleAppearance` is a struct rather than a flat `consoleDisplayName`
so that further per-instance appearance settings can join it without
another top-level field.

The field is optional and has no effect in Cloud, which serves one
console for all of an organization's regions. It is excluded from the
rollout hash and skipped when unset, so setting it does not roll
environmentd, and adopting a version of the operator that knows about it
does not roll instances that leave it unset.

Tests: adds `console_appearance_does_not_affect_the_rollout_hash` in
`mz-cloud-resources` and a `ConsoleAppearance` modification in
`test/orchestratord/mzcompose.py` covering the passthrough into
`app-config.json`, plus console unit tests for the tab title and the
app-config parsing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@djahandarie
djahandarie force-pushed the console-instance-appearance branch from d3b9dee to 150f13d Compare September 9, 2026 03:30
@djahandarie djahandarie changed the title console: let self-managed instances set a display name and accent color console: let self-managed instances set a display name Sep 9, 2026
@djahandarie

Copy link
Copy Markdown
Contributor Author

Split as asked.

This PR is now displayName only: 13 files, +263/−3. The CRD field, the orchestratord passthrough, the tab title, the rollout-hash test and the orchestratord modification test. No theme changes at all.

#38720 carries the accent work, stacked on this branch and left as a draft pending design direction on the palette. It also picks up your other two comments: red and green are gone, and crd-writer now derives an enum's values for the generated reference so they aren't hand-listed in a doc comment.

Rebased onto current main while I was in there. The screenshots above are from the combined version, so the accent ones belong to #38720 now.

@djahandarie

Copy link
Copy Markdown
Contributor Author

(Asked Claude to do one pass to handle the comments, sorry for the Claude-speak responses on here)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants