bb.settings
bb.settings presents settings pages, schema forms, and safe secret controls through kernel ports.
Purpose
The plugin owns the Settings application and the bb.settings.pages list surface. It declares no domain service.
The kernel owns each settings schema and the fixed bb.preferences and bb.secrets ports. bb.settings consumes these ports.
This plugin is presentation only. Other consumers can use preferences without the Settings application.
Surfaces
| ID | kind | replaceable | props contract sketch | notes |
|---|---|---|---|---|
bb.settings.pages |
list |
no | SettingsPageProps with pageId, ownerPluginId, route, compact, and navigate() |
Each claim adds one page. The user can hide or reorder pages. |
bb.settings.pages
The surface accepts ordered page registrations. The kernel keeps the claim and order records with the other surface claims.
import type { ComponentType } from "react";
export interface SettingsRoute {
pageId: string;
subpath: readonly string[];
}
export interface SettingsPageProps {
pageId: string;
ownerPluginId: string;
route: SettingsRoute;
compact: boolean;
navigate(next: SettingsRoute): void;
}
export interface SettingsPageRegistration {
/** Unique inside the claimant. The runtime forms `<pluginId>/<id>`. */
id: string;
label: string;
description?: string;
icon?: string;
order?: number;
component: ComponentType<SettingsPageProps>;
}The runtime sorts visible pages by the user order. It then uses order, the claimant ID, and the registration ID.
The default bb.settings implementation adds the General and Experiments pages. Other first-party plugins add domain pages through the same surface.
For each manifest settings schema, bb.settings also makes one generated plugin page. A custom page claim does not replace that generated page.
An author can omit the generated page in the manifest. The kernel manifest contract controls that choice.
Schema form boundary
The manifest declares the schema. See Kernel: plugin manifest.
The bb.settings plugin reads the validated schema from bb.plugins. It does not accept a second runtime schema registration.
The generated form uses the kernel bb.preferences port for non-secret fields.
It uses the kernel bb.secrets port for secret status.
The field sends a secret value directly to the kernel's human-only secret flow. The plugin never receives the value.
The form shows only configured, missing, saving, or error after a secret write. Logs and preference records never contain the value.
Services
| ID | kind | replaceable | default provider | purpose |
|---|---|---|---|---|
| — | — | — | — | This presentation plugin declares no service. |
Kernel port use
bb.settings consumes the fixed bb.preferences port.
The port reads, writes, clears, lists, reloads, and watches persistent preference cells.
The kernel owns preference storage, validation, defaults, revisions, and actor limits. The UI only presents these operations.
Dependency edges
The first-party bb.settings plugin declares these edges.
| edge | contract | reason |
|---|---|---|
required |
bb.preferences |
Reads and writes validated non-secret settings. |
required |
bb.plugins |
Reads validated manifest schemas and plugin status. |
required |
bb.secrets |
Shows secret status and follows set or unset changes. |
The plugin owns only presentation. It does not wrap or replace any of these ports.
Exports
Module imports select exact code. They do not follow the active surface or service winner.
| module | export | purpose |
|---|---|---|
@bb/settings/components |
SettingsPage |
Provides the standard page header and width rules. |
@bb/settings/components |
SettingsSection |
Groups related controls. |
@bb/settings/components |
SettingsRow |
Aligns a label, help text, control, and error text. |
@bb/settings/components |
SchemaSettingsForm |
Renders one validated kernel manifest schema. |
@bb/settings/components |
SecretSettingField |
Uses the kernel human-only secret flow and exposes status only. |
@bb/settings/hooks |
usePreference |
Adapts one profile or thread cell from bb.preferences. |
@bb/settings/hooks |
usePluginSettings |
Adapts all non-secret manifest settings from bb.preferences. |
import type {
JsonValue,
PreferenceAddress,
PreferenceWriteOptions,
} from "@get-bb/plugin/contracts";
export interface UsePreferenceState<T> {
value: T;
source: "stored" | "default";
revision: string | null;
isLoading: boolean;
error: Error | null;
set(next: T, options?: PreferenceWriteOptions): Promise<void>;
clear(options?: PreferenceWriteOptions): Promise<void>;
}
export function usePreference<T extends JsonValue>(
address: PreferenceAddress,
): UsePreferenceState<T>;
export interface UsePluginSettingsState {
values: Readonly<Record<string, JsonValue>> | undefined;
isLoading: boolean;
error: Error | null;
}
export function usePluginSettings(
pluginId: string,
): UsePluginSettingsState;usePluginSettings() omits all secret keys. A component uses SecretSettingField when it needs a secret control.
Host roles
bb.settings declares no host role. Its presentation code uses typed adapters for kernel ports.
Example
This plugin adds a settings page and reads a kernel preference. Its manifest declares the preference schema by the kernel rules.
{
"id": "acme.github",
"version": "2.0.0",
"claims": [{ "surface": "bb.settings.pages" }],
"requires": [
{ "service": "bb.preferences", "range": "^1" }
],
"artifacts": {
"app": "./dist/app.js"
}
}The omitted manifest settings block declares acme.github/reviewMode. See Kernel: plugin manifest.
// src/app.tsx
import { definePlugin } from "@get-bb/plugin/app";
import {
SettingsPage,
SettingsSection,
usePreference,
} from "@bb/settings";
const reviewMode = {
key: "acme.github/reviewMode",
scope: "profile",
} as const;
function GitHubSettings() {
const mode = usePreference<string>(reviewMode);
return (
<SettingsPage title="GitHub">
<SettingsSection title="Reviews">
<select
value={mode.value}
disabled={mode.isLoading}
onChange={(event) => void mode.set(event.currentTarget.value)}
>
<option value="fast">Fast</option>
<option value="balanced">Balanced</option>
<option value="strict">Strict</option>
</select>
</SettingsSection>
</SettingsPage>
);
}
export default definePlugin((api) => {
api.surfaces.provide("bb.settings.pages", {
id: "github",
label: "GitHub",
description: "Configure GitHub review behavior.",
order: 300,
component: GitHubSettings,
});
});usePreference() uses the fixed kernel port. bb.settings supplies the hook and form presentation only.
Covers
| old item ID | new contract/verb | note |
|---|---|---|
app.hooks.useSettings |
@bb/settings/hooks: usePluginSettings() |
The hook adapts bb.preferences, omits secrets, and reports errors. |
app.slots.settingsSection |
bb.settings.pages surface |
Each old section becomes an ordered settings page claim. |
app.contracts.PluginSettingsSectionProps |
SettingsPageProps |
Pages receive routing and compact-layout data. |
app.contracts.PluginSettingsSectionRegistration |
SettingsPageRegistration |
A list-surface registration replaces the fixed slot registration. |
app.contracts.PluginSettingsState |
UsePluginSettingsState |
The UI state adapts bb.preferences, reports errors, and keeps secret values out. |
app.contracts.PluginSettingsSectionRegistration.id |
SettingsPageRegistration.id |
The runtime qualifies the ID with the claimant plugin ID. |
app.contracts.PluginSettingsSectionRegistration.title |
SettingsPageRegistration.label |
The page label appears in navigation and the page header. |
app.contracts.PluginSettingsSectionRegistration.description |
SettingsPageRegistration.description |
The page can show the description below its heading. |
app.contracts.PluginSettingsSectionRegistration.component |
SettingsPageRegistration.component |
The component now receives SettingsPageProps. |