Settings extensions — plan-included, no Store tiles
Maintainer, 2026-10-04: "we have many things in the app store which actually link to the settings tab. in these cases, show on the front page how to use the feature, maybe with a video … then link to the settings. … we should not see individual apps for google maps etc. just settings tab." Two follow-ups in the same session: "you don't need to install these packages bit by bit. they should just come with the sub" and "up to the level of the sub of the user — if i am a pro user, i have a right to enter my google maps key, etc.", and on review: "it must go to settings apps. we must have for user, one for instance. then settings inside the corresponding apps."
This page records the scan and the plan. Status: proposed, no code changed yet.
The rule we are moving to
A settings extension is a package whose user-facing surface is configuration: a key, a provider choice, an account connection, a login. For those:
- No Store tile and no launcher app. The package keeps its Store root (that is where its code, guide and tests live), but the catalog and the Home launcher stop listing it as something to Get.
- No install. It is not copied into the viewer's space. Its settings appear as a section in
its settings home the moment the viewer's plan covers its
tier. - Plan-included, up to the viewer's tier. The gate is
SubscriptionFact.Covers(package.tier), evaluated live. A pro viewer sees every free, personal and pro extension; a free viewer sees the free ones and an upsell line for the rest. No_Accessrecord and no_Entitlementsrecord is minted. That keeps us inside "no one should have any access, just through subs and tiers" (2026-09-30,AccessThroughPlanTests) and away from the free grants on sight thatFreeGrantRetractionremoved. A downgrade hides the section and keeps the stored key, so an upgrade restores it. - A how-to front page instead of an app. The package's cover becomes "how to use it": a short guided tour of the screens to click, what to tell the agent, and one primary button, Open settings, deep-linking to the section in its settings home.
Purchasing is plans only
Maintainer, 2026-10-04: "so generally, the purchase functionality will be limited to purchasing the
pro / personal / whatever". What a person buys is a plan tier (personal, pro, team, …). No
package is bought on its own: a package's tier says which plan includes it, and holding that plan
is the whole entitlement. Settings extensions are the first application of this rule, not an
exception to it.
Measured 2026-10-04 across the fleet: exactly one package still carries a per-package price —
Manufacturing (MeshWeaver.Manufacturing, price: 900 CHF, tier: enterprise). Every other root is
priced through its tier already. Consequences for the Store:
- Checkout sells plans only.
PlanCheckout+ the subscription webhook stay. The per-packageStore/Orderpurchase path (OrderFulfilment's priced branch, a cover's Buy CTA) is retired onceManufacturingmoves to its tier (enterprise = contact sales), andpriceon a package root becomes a validation error invalidate-repos.py. - A cover offers the plan, never the package. Covered: Open (or Open settings). Not covered: Included in Pro → see plans.
- Coupons grant a plan or a trial of one, not a single package. Existing package-scoped coupons are honoured until they expire; new ones are plan-scoped.
- Eternal entitlement records stay for what was already bought individually, so nobody loses a past purchase.
The App Store disappears — it becomes "Manage apps"
Maintainer, 2026-10-04: "in instance admin, say 'Manage' then the app store will become a guide to managing the apps, including their settings", then "so app store will disappear in this sense. it will just be manage this instance" — "i.e. manage the apps on this instance." Once nothing but plans is sold, there is nothing left to shop for app by app, so there is no App Store surface for users at all:
| Who | Where | What they see |
|---|---|---|
| Every user | Home / the launcher | the apps their plan includes, nothing else. Each app's front page is its how-to (tour, what to tell the agent, Open settings). An app outside the plan is simply absent. Upgrading is the Plans page and nothing more |
| Instance admin | Admin app › Manage apps | every app on the instance with its tier and status (needs configuration / ready / error). Each app's page says what it needs to run and embeds its instance settings section inline (shared keys, registrations, defaults) |
| Anonymous visitor (public instance) | the Plans page | what each tier includes, as the storefront. It replaces the catalog |
The old store becomes the Add button of Manage apps (maintainer: "the 'store' must be the
'add'"). Manage apps lists the apps that are on this instance. Add opens the registry's
catalog of apps that are not here yet: the same cards, guide and tier as today's Store, but with
one action, Add to this instance, which runs the existing SystemInstall provisioning. Remove
is the inverse. Browsing and choosing apps is therefore an admin act for the instance, never a
user purchase.
Adding an app is what makes its settings exist. Settings sections are contributed by the app, so they appear the moment it is added and disappear when it is removed:
- The admin adds the app under Manage apps → its instance section appears on its page there.
- Its user sections appear in the user settings app (or the host app's Settings tab) for every user whose plan covers its tier.
- Values resolve user → instance: a user's own key wins, otherwise the instance key applies, otherwise the section says what is missing.
Example — Google Maps (pre-installed on the public instance): it is listed under Manage apps as added. The global admin opens it and sets one Maps key for the whole system, and every map on the instance renders with Google from then on. Today that key exists only in the deployment configuration, so setting it needs a redeploy. A user may still enter their own key in the user settings app › Maps, which is used for their maps instead.
Keys go to Key Vault (maintainer: "key goes to KV"), never into node content. The instance
section reuses the existing path in Hosting/Deployment/Source/SecretDialog.cs, rather than adding
a new one:
- The section shows a write-only paste box per secret the app declares (Google Maps:
maps-google-api-key). It renders only a mask and "set on …". - Saving files a
Hosting/InstanceActionwithrequestedAction: SetSecrets. The value is encrypted with the platform key protector at filing, and the control plane authorises the global admin. One operator Job writes it into the instance's vault (hosting-kv-set --file), waits for the CSI driver to sync it, and removes the Job and the ciphertext. - The app reads it through configuration (
Maps:Google:ApiKey). Each app's package declares its vault objects and their config keys, so the instance record maps them without a hand edit. Whether the portal must restart to pick up a new value, or reads the synced object live, is settled per app in phase 3. A key read live is preferred. - User keys (a user's own Maps key) follow the same rule, with a per-user vault object name. Phase 1 confirms the naming and the per-user read path.
Manage apps (the apps on this instance) is the instance settings app for apps, not a second
surface beside it. Users, access, plans and seats stay in their own Admin tabs. Each app's
instance section is the same UiContribution, rendered inside that app's management page, so an
admin reads what an app does and configures it in one place.
The Store package survives as the engine, not as a destination: plans, checkout,
subscriptions, licences, coupons, install-defaults and the tier gate. Its catalog, cover CTAs and
Extensions shelf are retired. Cover pages become the apps' front pages (users) and management pages
(admins).
Three settings homes — and only three
Every setting lives in exactly one of three places, chosen by whose setting it is:
| Home | Address | Holds | Who edits |
|---|---|---|---|
| User settings app | /{user}/Settings (the person app) |
what belongs to one person and no single app: my map keys, my channel links, my account connections, my signing authority | the person |
| Instance settings app | the Admin app (AdminAppNodeType) › Manage apps |
what belongs to the whole installation: the shared provider keys, the fallback map key and default renderer, bot/app registrations, system mail, payments | global admin |
| The app's own settings | the host app's Settings tab (e.g. AI/AiThreads › Settings) |
how one app works: the Threads app's models and harnesses, Signature's providers, HomeAssistant's connection | the app's user |
The home is not a new field: it is the package's existing hostedIn. {user}/Settings means
the user settings app, Admin the instance settings app, and an app path (AI/AiThreads) that app.
A package with both a personal and an instance half (Maps, the channels, Mail) contributes one
section to each. No setting is ever offered in two homes for the same scope, so nobody has to guess
where to look.
Scan — every Store/Plugin root in the fleet
Scanned 2026-10-04 at origin/main of MeshWeaver, MeshWeaver.Plugins, .Crm, .Education,
.FundReporting, .Manufacturing, .Reinsurance and .SocialMedia: 109 packages. Core ships no
Store/Plugin root. Crm, Education, FundReporting, Manufacturing and Reinsurance have no
settings-shaped package. Their keyword hits (Pricing's "simulation settings", Chess's "settings
locked") are false positives.
A — pure settings extensions: remove the tile, become a settings section
| Package | Today | Tier | User settings app | Instance settings app | App's own settings |
|---|---|---|---|---|---|
GoogleMaps |
app: true, opens GoogleMaps/Examples; key only in the deployment config |
free | Maps: my Google Maps key | Maps: fallback key, default renderer | — |
AppleMaps |
app: true, installs AppleMaps/MyMaps (holds the key) + Examples |
free | Maps: my Apple Maps Server key | Maps: fallback key | — |
OpenStreetMap |
app: true, opens Examples; needs no key |
free | — | Maps: the keyless default renderer | — |
Maps |
base MapControl, not an app |
free | hosts Maps | hosts Maps | — |
MyAi ("AI Settings") |
app: true, entry MyAi/Settings |
free | AI: my agent/skill/model sources | — | — |
Providers |
app: true, entry ProvidersApp/area/ProviderSetup |
free | — | AI › Providers: the shared keys and tiers | Threads › Settings › Models: my own keys |
Anthropic, OpenAI, AzureFoundry, AppleIntelligence |
hostedIn: AI/AiThreads, slot modelProvider, offered on the Extensions shelf |
free | — | shared key / endpoint | Threads › Settings › Models: rows (my key) |
ClaudeCode, Codex, Copilot, Cursor, Grok, OpenCode, Antigravity (+ Acp runtime) |
hostedIn: AI/AiThreads, slot harness, per-user install into {you}/Harness/* |
pro | — | CLI availability (feature flags) | Threads › Settings › Harnesses: enable + /login |
WebSearch |
tile; entry ProvidersOverview |
pro | — | AI › Web search: search key | Threads › Settings: on/off per thread default |
Teams, WhatsApp, iMessage |
tiles, "talk to your assistant on X" | personal | Channels: link my account / my Mac bridge | Channels: bot / Business API registration | — |
Voice |
tile; installs Voice/Agent, Prompt, Station |
pro | Channels › Voice: pair my satellite | — | — |
Mail |
tile; system mail + Graph mailbox tools | personal | Connections › Microsoft 365: my mailbox consent | Mail: system mail + intake | — |
Stripe |
tile; instance payments | free | — | Payments | — |
B — real apps with a settings half: keep the app, move the credentials
Each of these is a dashboard people use daily. The app stays and gets its own Settings tab, which holds its connection (OAuth client, key, account). The app's empty state links to that tab instead of walking the user through four fields on the app page. A credential that is the person's across apps (signing authority) stays in the user settings app.
| Package | Repo | Goes into the app's own Settings |
|---|---|---|
AppleWeather (MyWeather) |
Plugins | WeatherKit team/key id + .p8 |
AppleMusic (MyMusic) |
Plugins | MusicKit developer token / user token |
Google (MyGoogle) |
Plugins | Google OAuth client + consent |
ICloud (MyICloud) |
Plugins | app-specific password |
HomeAssistant (MyHome) |
Plugins | base URL + long-lived token |
Signature (MySignatures) |
Plugins | provider credentials. Signing authority stays in the user settings app, where it already is (Signature/PersonAppTabs/SigningAuthority) |
X, LinkedIn, YouTube |
SocialMedia | account / archive import |
C — adjacent, not settings (decide separately)
DefaultViews, EntityViews, GraphViews, Radzen, OgCard, Export, Analysis and
BusinessRules are app: true but open an Examples/Guide page. They are capability packs
(controls, view packs), not apps and not settings. Their natural home is the docs. Out of scope here
and listed so nobody re-scans them.
Consolidation — every package gets exactly one place
Maintainer, 2026-10-04: "see what you want to consolidate. we need order." Every one of the 109
Store/Plugin roots lands in exactly one of five places. Today about 40 of them present
themselves as tiles; after this, a user sees about a dozen apps.
| Place | What it is | Who sees it |
|---|---|---|
| App | a launcher tile you work in daily | users whose plan covers it |
| Section of an app | a module, channel or course inside a bigger app | users of that app |
| Settings section | in one of the three settings homes | per home (user / instance admin / app) |
| Developer app | examples, showcases, builder tools | builders (free) |
| Capability | no surface of its own: a menu action, a control, a runtime | nobody as a tile; listed in Manage apps |
Apps — what stays a tile (13 + vertical suites)
| App | Absorbs | Notes |
|---|---|---|
Threads (AI) |
Chat; providers and harnesses as its Settings |
the AI home |
Learning (Edu) |
all 14 courses (Education's 11; Reinsurance's RiskTransfer, SwissSolvencyTest, ReinsurancePractice), LearningRoadmap, Training tours |
courses stop being tiles; one catalog inside the app, filtered by plan and language |
| Approvals | ||
| Signature | providers in its Settings; signing authority in user settings | |
| Feedback | ||
CRM (Crm) |
||
Social Media (SocialMedia) |
LinkedIn, X, YouTube, Marketing |
networks become channels inside one app, each with its connection in the app's Settings |
| Google, iCloud | personal data apps; connection in the app's Settings | |
| Home Assistant, Apple Music, Apple Weather | personal apps; connection in the app's Settings | |
| Games | Chess, RolePlay, QualityTime + QualityTimeDe |
one tile; the language twin is picked by the viewer's language |
| Reinsurance (enterprise) | Reinsurance, Claims, Underwriting, Pricing, LossModelling, Planning, Ifrs17, SST, ILS, EventMonitoring, Portfolio, PortfolioOptimization, TaskManagement, ReinsuranceDemo, Cornerstone |
one suite app, modules as sections |
| Manufacturing, Fund reporting (enterprise) | one app per vertical repo |
Admin — inside the Admin app, never a user tile
| Admin section | Packages |
|---|---|
| Manage apps (+ Add) | the Store engine; every app's instance settings |
| Operations | Hosting, Governance, BuildServer, AzureCostManagement, Observability |
| Instance settings | Stripe (payments), Mail (system mail), shared AI keys, map keys, channel registrations |
Settings sections — no tile (see the scan above)
GoogleMaps, AppleMaps, OpenStreetMap, Maps · Anthropic, OpenAI, AzureFoundry,
AppleIntelligence, Providers, MyAi, WebSearch · the seven harnesses + Acp · Teams,
WhatsApp, iMessage, Voice, Mail (personal half) · Notifications (already the person app's
notification preferences) · Mcp (user settings › Connections: connect an MCP client).
Developer app — one tile for builders
| Section | Packages |
|---|---|
| Examples | DefaultViews, EntityViews, GraphViews, Radzen, Analysis, OgCard, Export, the three map galleries |
| Showcases | Northwind, DoublePendulum, FractalStars, ThreeBody |
| Builder tools | DataModelling (model browser), BusinessRules (guide), Testing (in-mesh xunit) |
Each package contributes its section the same way settings sections are contributed, so a section exists only when its package is on the instance.
Capabilities — no surface of their own
Essentials, Publish, Import, Indexing, Collaboration (review/track changes in any
document), Export (the PDF/DOCX menu action — its gallery is in Developer), OgCard (markdown
embed), RemoteControl (the Present on screen menu action), Video (the explainer-video skill),
Training (the tour player every front page uses), AzureBlob, Hosting.Instance. They appear
only in Manage apps, where an admin can see they are present.
Order of the work
- Settings sections + three homes (phases 1–3 above). This removes the largest group of false tiles, about 25.
- Developer app. It is additive, so it can run in parallel with 1, and it removes 15 tiles.
- Suites: Learning (courses), Social Media, Games, Reinsurance. Each is one PR per suite in its
own repo. The suite app lists its sections, and the members drop
app: true. - Admin › Operations + Manage apps (+ Add), then retire the user-facing catalog.
What exists already (reuse, do not rebuild)
- Person app
/{user}/Settingswith core tabs Profile, Account, Preferences and Sharing (PersonApp.AddPersonAppTabs), plus contributed tabs fromUiContributionnodes withcontext: PersonApp(SettingsMenuItemsExtensions.ContributedPersonAppTabs). Signature's Signing authority and the Store's Extensions tab use it today. - In-app extensions (core #5889, Plugins #2553):
hostedIn+extensionSlotonPluginContent, the Store shelfStore/area/Extensions?id={host}#{slot}, and therequireAddressAccessgate. The shelf currently sells each extension with its ownCoverCta, and that is the per-package Get this plan removes. - Plan coverage:
Subscriptions.Effective+SubscriptionFact.Covers(tier)(Store/Licensing), already used by the paywall (PluginLayoutAreas) and the catalog (CatalogPlanCoverage). - Guided tours:
Training/Touris a step player with live embedded screens and a "what to notice" list, authored as data. It is the "video highlighting the GUI".Video(pro) renders a narrated MP4 from a script when a real video is wanted. - Admin app (
AdminAppNodeType,AddAdminAppTab/RelocateSettingsTabsToAdminApp) for the instance-wide halves (Stripe, system mail, the shared provider keys).
Plan
Phase 1 — the contract (core + Store)
PluginContent.surface: "settings"(core, besidehostedIn/extensionSlot): declares a settings extension.hostedInnames its home ({user}/Settings,Admin, or an app path), a list where a package has both a personal and an instance half; each half is aUiContributionnaming the section and the package's own layout area. One field, read by every consumer, so the catalog, the launcher and the three settings homes cannot disagree.- New gate
requireTieronUiContribution(core vocabulary, Store evaluates it): the section shows iffSubscriptions.Effective(viewer).Covers(package.tier). It replacesrequireAddressAccessfor settings extensions, because Read on the package root presupposed an install. Tests: free/personal/pro × package tier matrix; lapsed plan → hidden; key retained. - The three homes host the sections. User settings app:
Maps,AI,Channels,Connections(context: PersonApp). Instance settings app:Maps,AI,Channels,Mail,Payments(Admin app tabs, global admin only). App settings: a Settings tab on each host app, fed by a newcontext: AppSettingscontribution addressed to that app (AI/AiThreadsfirst, replacing today's Extensions shelf there). Each lists the covered sections, and the uncovered ones as one upsell line ("Included in Pro"), never as a buy button.
Phase 2 — the App Store goes, Manage apps comes
- Home launcher skips
surface: settingsroots (StoreCatalogLayoutAreas, launcher).app: trueis removed from every A-row package. - The user-facing catalog and the
Extensionsshelf are retired; the launcher lists the apps the plan covers, and the Admin app gains Manage apps (every app, status, inline instance settings) with Add (the registry catalog, Add to this instance viaSystemInstall) and Remove. The per-packageCoverCta/Get is retired, and the Plans page lists what each tier includes. - Migration: existing per-user installs (
{you}/Harness/*,AppleMaps/MyMaps, …) and their stored keys are read in place by the new sections. No re-entry. Retire the copies only after the section reads them, throughStore/InstallRequestuninstall, not by hand.
Phase 3 — the how-to front page per package
- Each A-row package's cover
bodyis rewritten to how to use it: what it does in one line, aTraining/Tour(3–5 steps: open Settings → the tab → paste the key → try it, with the exact sentence to tell the agent, e.g. "show the office on a map"), then Open settings. AVideo-rendered MP4 is added where a moving demo helps (harness/login, channel linking). - Maps-specific: the instance Google Maps key is set from Manage apps › Google Maps into Key Vault
(
SetSecrets) instead of a deployment edit, and a user's own key from the user settings app › Maps. Today neither the admin nor a user can set one without a redeploy.
Phase 4 — B-row apps
- Give each B-row app its own Settings tab (the same
AppSettingscontribution) and move its connection there. The app's empty state becomes "Connect in Settings →". The apps keep their tiles.
Order and repos
| Step | Repo | Gate |
|---|---|---|
| 1–2 contract + gate | MeshWeaver (core) then Store | sealed set before Plugins consumes it (Core-ref:) |
| 3–6 three homes, catalog, shelf, migration | core (AppSettings context, Admin tabs) + Plugins (Store, AI, Maps) |
Store Tests area: tier matrix, migration reads old keys |
| 7–8 covers + tours, Maps key | Plugins (each A-row package) | Training/Tour renders; render_area of each cover |
| 9 B-row connections | Plugins + SocialMedia | per-app Tests |
After each landing, recycle the per-node hubs that serve the changed types: Store/Catalog,
Store/Plugin, the person app on a test user, the Admin app, AI/AiThreads. Then verify on the public instance as
a free user and as a pro user that the sections, the upsell and the Open-settings links behave as
stated above.
Rule this amends
AGENTS.md ("publishing grants nobody anything") says content is reached through a plan tier and
only once the user acquires it. For surface: settings packages the plan tier alone is the
acquisition: no Get, no install, no minted grant. Phase 1 updates that sentence with the scoped
exception, so the rule and the code are changed together.
Decisions
- Tiers stay as they are (maintainer, 2026-10-04: "you can keep them free" — the pro user
and the Google Maps key were "just an example" of the rule, not a tier change).
GoogleMaps,AppleMapsandOpenStreetMapremainfree, so every signed-in user may enter their own map key. The other A-row tiers are unchanged too: providers free, Teams/WhatsApp/iMessage/Mail personal, harnesses/WebSearch/Voice pro. - Google and iCloud stay apps (B row) unless decided otherwise: their agenda views are used directly, and their connection moves into each app's own Settings tab.
- C-row view packs (DefaultViews, Radzen, …) → the Developer app (see Consolidation).
- Open: Add vs install-defaults. Today every instance installs each newly listed package on
its next install-defaults pass (
InstanceAutoRegistrationService.InstallDefaults). With Add, should new packages instead wait under Add until an admin adds them, keeping only a platform core set automatic? This changes running instances, so it needs the maintainer's call. - Courses → the Learning app (see Consolidation).