Repository navigation
feat(cli,cloud-connection): OS_CONSOLE_MAP_STYLE_URL names the deployment's default map style - #26162
Conversation
…onfig and the Console CSP (red) Claude-Session: https://claude.ai/code/session_011ApYCfy4gzYfuYeXX38EcT Co-authored-by: Claude <noreply@anthropic.com>
…ment's default map style RuntimeConfigPlugin serves it as mapStyleUrl on /api/v1/runtime/config and the Console mount adds its origin to the document's connect-src, both read at boot through one exported reader. A value that is not an absolute https: URL on a host name, without credentials, refuses the boot naming the env. Claude-Session: https://claude.ai/code/session_011ApYCfy4gzYfuYeXX38EcT Co-authored-by: Claude <noreply@anthropic.com>
…ap style readers to one verdict The published console subpath imports nothing but Node built-ins, so the Console mount restates the OS_CONSOLE_MAP_STYLE_URL contract instead of importing it from cloud-connection; the CSP suite holds the two readers to the same verdict and refusal text on every value it names. Claude-Session: https://claude.ai/code/session_011ApYCfy4gzYfuYeXX38EcT Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 34 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin fc338fadc31d2d5d2b9a17d1570ac11cf0977ff6 && git checkout fc338fadc31d2d5d2b9a17d1570ac11cf0977ff6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a335f1defbea57ffe552d2e9efc5ad8d56a2e76a ef163a1bbc2a9d7cc1d62197461494446143791c && git checkout -B drift-repro a335f1defbea57ffe552d2e9efc5ad8d56a2e76a && git merge --no-ff ef163a1bbc2a9d7cc1d62197461494446143791c
node scripts/docs-audit/affected-docs.mjs --json a335f1defbea57ffe552d2e9efc5ad8d56a2e76a
|
席位核对 · ACCEPT ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, Fixes #26001, patch changeset, mergeable clean. |
| Files | packages/cli/src/utils/console.ts (+ CSP test); packages/cloud-connection/src/console-map-style.ts (new) and runtime-config-plugin.ts (+ test); content/docs/deployment/environment-variables.mdx; one changeset. |
| Fix | One operator env, OS_CONSOLE_MAP_STYLE_URL. RuntimeConfigPlugin serves it as the top-level mapStyleUrl, absent when unset. The Console mount adds the value's origin to CSP connect-src at boot. Both readers refuse the same values: whitespace, a non-absolute URL, a non-https: scheme, embedded credentials (the anonymous door would publish them, and the refusal text redacts them), and a host a CSP cannot allow. Demotiles stays allow-listed. Per ruling A there is no Setup setting, no settings key, no spec key and no per-request CSP. |
| Departures (accepted) | (1) Two readers plus a parity test instead of one shared reader: @objectstack/cli/console may import Node built-ins only (published-subpath-console.pin.test.ts). (2) RuntimeConfigPlugin refuses from start(), because serve.ts swallows constructor throws. (3) The changeset is graded patch, as Metadata-protocol: none grades it. |
| Evidence | 38 check runs succeeded and 10 were expected skips on this head, 0 failed. Dev: 99/99 derived gates after a full build, cli unit 4,873/4,873, cloud-connection 745/745, and a live dev:crm probe of the refusal, the served value and connect-src. |
| Follow-ups filed | domain:ui sub-card #26163 (plugin-map reads mapStyleUrl, Blocked-by: #26001). Cloud-lane card #26164 (status:parked): whether cloud's objectos-runtime serves the Console and runtime-config from one process env, plus cloud's env docs. |
| Landing | 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay. |
Generated by Claude Code
Requested by Leehom · project thread
Fixes #26001
Clause-②: no
Metadata-protocol: none
The server half of #26001 under ruling 6413991421 (letter A, operator env only). One environment variable names a deployment's default map style, so an intranet deployment, or one that forbids a third-party tile host, can stop depending on the public demo style host:
What the one value does
RuntimeConfigPluginserves it verbatim as the top-levelmapStyleUrlonGET /api/v1/runtime/config(and its legacy alias). Unset or empty, the key is absent, the same default-absent shape asbranding.stageandupgradeUrl, and every other key is byte-identical. The payload's doc block declares the field.connect-src, read once at boot besideOS_CONSOLE_CSP_CONNECT_SRC. The default demo host stays allowed when the env is set: a view may still name it in its ownmapStyle, and a Console build that does not readmapStyleUrlyet still loads its built-in default.https:URL on a host name, with no user name, password or whitespace. Anything else refuses the boot with a message that namesOS_CONSOLE_MAP_STYLE_URL(credentials are left out of the message).RuntimeConfigPluginrefuses fromstart(), not the constructor: the CLI's cloud wiring inserve.tswraps the construction in a try/catch that turns a throw into a warning and a missing door.No Setup setting, no settings-manifest key, no spec key, no per-request CSP, no
ui/**, nodocs/adr/**. Only the operator can widen the Console'sconnect-src.Name.
OS_CONSOLE_MAP_STYLE_URLfollows AGENTS.md Prime Directive #9 for a config value (OS_{DOMAIN}_{NAME}), in theCONSOLEdomain besideOS_CONSOLE_CSP_CONNECT_SRC. The payload key ismapStyleUrl: the renderer sub-card reads it as its fallback before the demo style.One contract, two readers
The order asked for one value read by both sides. They cannot share code:
packages/cli/src/utils/console.tsis published as@objectstack/cli/consoleand imports Node built-ins only, whichtest/published-subpath-console.pin.test.tspins against the packed tarball (an import of@objectstack/cloud-connectionreds it withERR_MODULE_NOT_FOUND, measured on the first push). So the contract lives inpackages/cloud-connection/src/console-map-style.tsand the Console mount restates it asreadMapStyleOrigin.console.content-security-policy.test.tsholds the two readers to the same verdict, refusal text included, on 11 values (4 accepted, 7 refused), and asserts that the CSP names the origin of the very URL the door serves.Two processes
os start: the parent spawnsos servewith its env, and bothRuntimeConfigPlugin(CLI-wired or the host config's own) and the Console static plugin run in that child, reading the sameprocess.envat start. They agree.--no-console/OS_DISABLE_CONSOLE: the door serves the URL and there is no Console document, so nothing can disagree.ObjectOSEnvironmentPluginpresent): the CLI wires noRuntimeConfigPlugin, the host wires its own. They disagree only when the process that serves the Console document is not the one that serves/api/v1/runtime/configand the two carry different env. Then the CSP can name an origin the door does not serve (harmless), or the door can serve a URL the CSP blocks (a grey map). No such split exists in this repository.Tests (red first)
packages/cloud-connection/src/runtime-config-map-style.test.ts(new): onmain10 failed / 3 passed (the 3 absence controls pass there, as they should); with the fix 13/13.packages/cli/src/utils/console.content-security-policy.test.ts(extended): onmain7 failed / 21 passed; with the fix 39/39, including the 11 parity cases.http:(scripts/ablation-replace.mjs, anchor 1 to 0, blob912ed07b6027toaac41d764ec4): 2 red (the boot refusal and the parity case forhttp:); restored, blob back to HEAD's,git diff HEADempty.ef163a1bb:pnpm --filter @objectstack/cloud-connection test59 files / 745 tests passed;typecheckexit 0.pnpm --filter @objectstack/cli exec vitest run --project unit333 files / 4873 tests passed (the integration tier is declared to CI);typecheckexit 0. eslint overpackages/cliandpackages/cloud-connectionwith--no-inline-config: exit 0.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon this diff, run at headef163a1bb: 99 derived, 99 run, all exit 0.--ranreconciliation: 99 accounted for, 0 NOT MEASURED, 0 unrun. Four first exited 3 on prerequisites and were re-run once those were met:check-plugin-teardown-shape --self-test(the positive-control commit fetched on this shallow clone),check:i18n-coverage(the example closure built),check:skill-examplesandcheck:dual-build-cjs-loads(a fullpnpm build, 119/119 tasks).Live probe
pnpm dev:crm -- --freshafter a full build andpnpm console:build, each server on its own port and stopped afterwards:OS_CONSOLE_MAP_STYLE_URL=http://tiles.example.com/style.json: the boot exits 1 withPlugin com.objectstack.runtime.runtime-config failed to start - rollback complete: OS_CONSOLE_MAP_STYLE_URL is "http://tiles.example.com/style.json", which uses http: rather than https: ....OS_CONSOLE_MAP_STYLE_URL=https://tiles.example.com/styles/basic/style.json:GET /api/v1/runtime/configservesmapStyleUrlwith that value (keyscloudUrl, mapStyleUrl, singleEnvironment, features, branding, telemetry), andGET /_console/answers 200 withconnect-src 'self' https://demotiles.maplibre.org https://tiles.example.com.mapStyleUrlkey, andconnect-src 'self' https://demotiles.maplibre.org, as onmain.Changeset
.changeset/26001-console-map-style-url.md,patchfor@objectstack/cloud-connectionand@objectstack/cli. The order saidminor, butMetadata-protocol: nonegrades every entrypatchunder the level axis ofcheck-changeset-no-major.mjs(none-minoris exit 1), and the change adds no authorable key.Acceptance notes
ui/packages/plugin-maprenderer readingmapStyleUrlbefore its demo default. Until that lands, map views keep the built-in style. The seat files it as adomain:uisub-card blocked by this card.OS_CONSOLE_CSP_CONNECT_SRC; the env reference row says so.content/docs/deployment/environment-variables.mdxgains theOS_CONSOLE_MAP_STYLE_URLrow, and theOS_CONSOLE_CSP_CONNECT_SRCrow now lists its origin among the sources already allowed.Generated by Claude Code