Skip to content

feat: add deployment setting to disable the Codernauts game - #27662

Merged
bartekgatzcoder merged 7 commits into
mainfrom
bartek/hide-codernauts-toggle
Sep 2, 2026
Merged

feat: add deployment setting to disable the Codernauts game#27662
bartekgatzcoder merged 7 commits into
mainfrom
bartek/hide-codernauts-toggle

Conversation

@bartekgatzcoder

@bartekgatzcoder bartekgatzcoder commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Adds a codernauts_enabled runtime setting (default: enabled) that controls whether the Codernauts game link appears in the user dropdown menu. A toggle at the bottom of the Deployment > Appearance page controls it, and the value persists in site_configs, so it survives restarts and upgrades without any CLI flags or environment variables.

The setting works on all licenses: GET /api/v2/appearance reports it from both the enterprise and the default (AGPL) appearance fetchers, and PUT /api/v2/appearance persists it gated only by the deployment-config RBAC permission, not by an entitlement. On the Appearance page the toggle renders outside the Premium paywall introduced in #27948; the other appearance fields keep their existing licensing behavior.

Note: a pure AGPL-only build of coderd does not register the /api/v2/appearance routes at all, so the toggle cannot be saved there. The standard coder server binary is unaffected regardless of license state.

Implementation notes
  • coderd/database/queries/siteconfig.sql: new GetCodernautsEnabled / UpsertCodernautsEnabled queries backed by a codernauts_enabled key in site_configs; defaults to true when unset (no migration needed).
  • coderd/database/dbauthz: read has no authz checks (matching other appearance reads); write requires ResourceDeploymentConfig update. Coverage added to dbauthz_test.go.
  • codersdk: codernauts_enabled added to AppearanceConfig and UpdateAppearanceConfig.
  • coderd/appearance: the default fetcher takes a database.Store and reads the setting from the database so unlicensed deployments serve it too.
  • enterprise/coderd/appearance.go: fetches the value in Fetch and persists it in putAppearance.
  • Frontend: "Codernauts game" toggle section on Deployment > Appearance saving immediately via the existing appearance mutation; codernauts_enabled threaded from useDashboard().appearance through Navbar to UserDropdownContent, which renders the menu item only when enabled. Storybook stories with play functions cover toggling (entitled and not) and the dropdown hiding the link when disabled.

🤖 This PR was generated by Coder Agents on behalf of @bartekgatzcoder.

Adds a hide_codernauts runtime setting persisted in site_configs and
exposed through the appearance API. A new toggle at the bottom of the
Deployment > Appearance page controls it, and the user dropdown skips
the Codernauts menu item when it is set. The setting works on all
licenses: the default appearance fetcher reads it from the database
too, so no license entitlement is required.
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

Comment thread coderd/database/queries/siteconfig.sql Outdated
VALUES (
'hide_codernauts',
CASE
WHEN sqlc.arg(hide)::bool THEN 'true'

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.

Suggested change
WHEN sqlc.arg(hide)::bool THEN 'true'
WHEN @hide::bool THEN 'true'

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 adeb84d. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment thread coderd/database/queries/siteconfig.sql Outdated
)
ON CONFLICT (key) DO UPDATE
SET value = CASE
WHEN sqlc.arg(hide)::bool THEN 'true'

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.

Suggested change
WHEN sqlc.arg(hide)::bool THEN 'true'
WHEN @hide::bool THEN 'true'

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 adeb84d. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment thread coderd/database/queries/siteconfig.sql Outdated
Comment on lines +60 to +61
-- GetHideCodernauts returns whether the Codernauts game link is hidden
-- from the user dropdown menu. Defaults to false when unset.

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.

Suggested change
-- GetHideCodernauts returns whether the Codernauts game link is hidden
-- from the user dropdown menu. Defaults to false when unset.

yap

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.

Removed in adeb84d. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment thread codersdk/deployment.go Outdated
ServiceBanner BannerConfig `json:"service_banner"`
AnnouncementBanners []BannerConfig `json:"announcement_banners"`
SupportLinks []LinkConfig `json:"support_links,omitempty"`
// HideCodernauts hides the Codernauts game link in the user dropdown menu.

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.

Suggested change
// HideCodernauts hides the Codernauts game link in the user dropdown menu.

yap

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.

Removed in adeb84d. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment thread codersdk/deployment.go Outdated
// Deprecated: ServiceBanner has been replaced by AnnouncementBanners.
ServiceBanner BannerConfig `json:"service_banner"`
AnnouncementBanners []BannerConfig `json:"announcement_banners"`
// HideCodernauts hides the Codernauts game link in the user dropdown menu.

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.

Suggested change
// HideCodernauts hides the Codernauts game link in the user dropdown menu.

yap

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.

Removed in adeb84d. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment thread coderd/appearance/appearance.go Outdated
Comment on lines +17 to +18
// database may be nil when no store is available, in which case
// runtime settings fall back to their zero values.

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.

why tolerate nil?

this is classic "yap", words for the sake of words without actually saying what the recipient wants to hear. I do not need to be told that nil means a store in unavailable, I need to know why we would tolerate that. what benefit to we get out of letting it be optional instead of just requiring it?

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.

Fair point, there was no real benefit. adeb84d drops the nil tolerance: the fetcher now requires a store, and site.New passes opts.Database. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment on lines +315 to +324
export const CodernautsShown: Story = {
play: async ({ canvasElement, step }) => {
await step("shows the Codernauts link by default", async () => {
await openDropdown(canvasElement);
expect(
screen.getByRole("menuitem", { name: "Codernauts" }),
).toBeInTheDocument();
});
},
};

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.

we don't need a specific story for this. it's already captured in every other story.

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.

Removed the CodernautsShown story in adeb84d. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment thread site/site.go Outdated
Comment on lines +92 to +94
// No database is available here; coderd replaces this fetcher
// with a database-backed one during startup.
f := appearance.NewDefaultFetcher(nil, opts.DocsURL)

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'm pretty sure it's just opts.Database. we definitely have a database handle somewhere in here, because we use it for fetching user data/token state/etc.

Database database.Store

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.

Correct, it is. adeb84d passes opts.Database and removes the comment. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Use the @arg sqlc shorthand, drop comments that restate the code,
require a database store in the default appearance fetcher by passing
site.Options.Database, and remove the redundant CodernautsShown story.
…s-toggle

# Conflicts:
#	site/src/pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.stories.tsx
#	site/src/pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx
Rename hide_codernauts to codernauts_enabled across the API, database
queries, and frontend. The toggle now reads "Codernauts game" with a
neutral description, defaults to on, and switching it off disables the
game.
@bartekgatzcoder bartekgatzcoder changed the title feat: add deployment setting to hide the Codernauts game feat: add deployment setting to disable the Codernauts game Aug 19, 2026
Covers the upgrade path: with no stored site config the game is
enabled, disabling works without a license, and the stored value
survives switching to the licensed appearance fetcher.
Comment on lines +3653 to +3656
func (q *querier) GetCodernautsEnabled(ctx context.Context) (bool, error) {
// No authz checks
return q.db.GetCodernautsEnabled(ctx)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
func (q *querier) GetCodernautsEnabled(ctx context.Context) (bool, error) {
// No authz checks
return q.db.GetCodernautsEnabled(ctx)
}
func (q *querier) GetCodernautsEnabled(ctx context.Context) (bool, error) {
return q.db.GetCodernautsEnabled(ctx)
}

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.

Applied in 51a9b27. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Comment thread enterprise/coderd/appearance_test.go Outdated
Comment on lines +61 to +62
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I can't remember off the top of my head but is this definitely the intended way to create a context for a test in coder/coder?

Regardless it should be moved to just before first use (so right before basicUserClient.Appearance(ctx). Otherwise coderdentest.New and coderdtest.CreateAnotherUser start ticking down the timer and we're more likely to get a flake.

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.

Good catch. Switched to testutil.Context (the repo convention, with automatic cancel on cleanup) and moved it to right before the first use so setup no longer eats into the timeout. Done in 51a9b27. 🤖 (Coder Agents on behalf of @bartekgatzcoder)

Drop the no-authz comment from GetCodernautsEnabled and use
testutil.Context created right before first use in the Codernauts
test to avoid eating into the timeout during setup.
@github-actions github-actions Bot added the stale This issue is like stale bread. label Aug 27, 2026
@github-actions github-actions Bot closed this Aug 31, 2026
@bartekgatzcoder bartekgatzcoder removed the stale This issue is like stale bread. label Sep 2, 2026
…s-toggle

# Conflicts:
#	site/src/modules/dashboard/Navbar/UserDropdown/UserDropdownContent.tsx
#	site/src/pages/DeploymentSettingsPage/AppearanceSettingsPage/AppearanceSettingsPageView.tsx
@bartekgatzcoder
bartekgatzcoder marked this pull request as ready for review September 2, 2026 09:29
@bartekgatzcoder
bartekgatzcoder merged commit 55340de into main Sep 2, 2026
36 checks passed
@bartekgatzcoder
bartekgatzcoder deleted the bartek/hide-codernauts-toggle branch September 2, 2026 09:29
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants