You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 21075ce
Browse filesBrowse the repository at this point in the historyBrowse files
feat(cli,cloud-connection): OS_CONSOLE_MAP_STYLE_URL names the deployment's default map style (#26162)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1X9ndwPy9fuaXy5hbjAwpdcG)_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:
```sh
OS_CONSOLE_MAP_STYLE_URL=https://tiles.example.com/styles/basic/style.json
```
## What the one value does
1. **Runtime-config.** `RuntimeConfigPlugin` serves it verbatim as the
top-level `mapStyleUrl` on `GET /api/v1/runtime/config` (and its legacy
alias). Unset or empty, the key is absent, the same default-absent shape
as `branding.stage` and `upgradeUrl`, and every other key is
byte-identical. The payload's doc block declares the field.
2. **Console CSP.** The Console mount adds the URL's origin to the
document's `connect-src`, read once at boot beside
`OS_CONSOLE_CSP_CONNECT_SRC`. The default demo host stays allowed when
the env is set: a view may still name it in its own `mapStyle`, and a
Console build that does not read `mapStyleUrl` yet still loads its
built-in default.
3. **Refusals.** The value must be an absolute `https:` URL on a host
name, with no user name, password or whitespace. Anything else refuses
the boot with a message that names `OS_CONSOLE_MAP_STYLE_URL`
(credentials are left out of the message). `RuntimeConfigPlugin` refuses
from `start()`, not the constructor: the CLI's cloud wiring in
`serve.ts` wraps 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/**`, no `docs/adr/**`. Only the operator can widen the
Console's `connect-src`.
**Name.** `OS_CONSOLE_MAP_STYLE_URL` follows AGENTS.md Prime Directive
#9 for a config value (`OS_{DOMAIN}_{NAME}`), in the `CONSOLE` domain
beside `OS_CONSOLE_CSP_CONNECT_SRC`. The payload key is `mapStyleUrl`:
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.ts` is published as
`@objectstack/cli/console` and imports Node built-ins only, which
`test/published-subpath-console.pin.test.ts` pins against the packed
tarball (an import of `@objectstack/cloud-connection` reds it with
`ERR_MODULE_NOT_FOUND`, measured on the first push). So the contract
lives in `packages/cloud-connection/src/console-map-style.ts` and the
Console mount restates it as `readMapStyleOrigin`.
`console.content-security-policy.test.ts` holds 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
- Single `os start`: the parent spawns `os serve` with its env, and both
`RuntimeConfigPlugin` (CLI-wired or the host config's own) and the
Console static plugin run in that child, reading the same `process.env`
at start. They agree.
- `--no-console` / `OS_DISABLE_CONSOLE`: the door serves the URL and
there is no Console document, so nothing can disagree.
- A runtime-host kernel (`ObjectOSEnvironmentPlugin` present): the CLI
wires no `RuntimeConfigPlugin`, 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/config` and 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): on `main` 10 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): on `main` 7 failed / 21 passed; with the fix 39/39,
including the 11 parity cases.
- Ablation (one-off, not kept): let the Console reader accept `http:`
(`scripts/ablation-replace.mjs`, anchor 1 to 0, blob `912ed07b6027` to
`aac41d764ec4`): 2 red (the boot refusal and the parity case for
`http:`); restored, blob back to HEAD's, `git diff HEAD` empty.
- At head `ef163a1bb`: `pnpm --filter @objectstack/cloud-connection
test` 59 files / 745 tests passed; `typecheck` exit 0. `pnpm --filter
@objectstack/cli exec vitest run --project unit` 333 files / 4873 tests
passed (the integration tier is declared to CI); `typecheck` exit 0.
eslint over `packages/cli` and `packages/cloud-connection` with
`--no-inline-config`: exit 0.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` on this diff, run at head `ef163a1bb`: 99
derived, 99 run, all exit 0. `--ran` reconciliation: 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-examples` and `check:dual-build-cjs-loads` (a full `pnpm
build`, 119/119 tasks).
## Live probe
`pnpm dev:crm -- --fresh` after a full build and `pnpm 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 with `Plugin 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/config` serves `mapStyleUrl` with that value (keys
`cloudUrl, mapStyleUrl, singleEnvironment, features, branding,
telemetry`), and `GET /_console/` answers 200 with `connect-src 'self'
https://demotiles.maplibre.orghttps://tiles.example.com`.
- Env unset (control): no `mapStyleUrl` key, and `connect-src 'self'
https://demotiles.maplibre.org`, as on `main`.
## Changeset
`.changeset/26001-console-map-style-url.md`, `patch` for
`@objectstack/cloud-connection` and `@objectstack/cli`. The order said
`minor`, but `Metadata-protocol: none` grades every entry `patch` under
the level axis of `check-changeset-no-major.mjs` (`none-minor` is exit
1), and the change adds no authorable key.
## Acceptance notes
- Not done here, by scope: the `ui/packages/plugin-map` renderer reading
`mapStyleUrl` before its demo default. Until that lands, map views keep
the built-in style. The seat files it as a `domain:ui` sub-card blocked
by this card.
- A style whose tiles, sprites or glyphs live on other hosts still needs
those hosts in `OS_CONSOLE_CSP_CONNECT_SRC`; the env reference row says
so.
- `content/docs/deployment/environment-variables.mdx` gains the
`OS_CONSOLE_MAP_STYLE_URL` row, and the `OS_CONSOLE_CSP_CONNECT_SRC` row
now lists its origin among the sources already allowed.
---
_Generated by [Claude
Code](https://claude.ai/code/session_011ApYCfy4gzYfuYeXX38EcT)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat: `OS_CONSOLE_MAP_STYLE_URL` names a deployment's default map style, served on runtime-config and allowed by the Console's content-security-policy
7
+
8
+
Clause-②: no
9
+
Metadata-protocol: none
10
+
11
+
A map view that names no `mapStyle` falls back to a built-in style on a public demo host, so a deployment without public internet, or one whose policy forbids a third-party tile host, shows a grey map. An operator can now name the deployment's own style document once:
That one value does two things, both read at boot:
18
+
19
+
-`GET /api/v1/runtime/config` serves it verbatim as the top-level `mapStyleUrl`, the deployment's default map style. Unset, the key is absent and every other key is unchanged.
20
+
- The Console document's `connect-src` gains the URL's origin. The default demo host stays allowed beside it.
21
+
22
+
The value must be an absolute `https:` URL on a host name, with no user name, password or whitespace. Anything else stops the server at boot with a message naming the variable, the same way a malformed `OS_CONSOLE_CSP_CONNECT_SRC` entry does. The runtime-config door is anonymous, so the URL is public once set.
23
+
24
+
It is an operator setting only: there is no Setup setting and no metadata key for it, so only the person who runs the deployment can widen the Console's content-security-policy. A style whose tiles, sprites or glyphs live on other hosts still needs those hosts in `OS_CONSOLE_CSP_CONNECT_SRC`.
25
+
26
+
The Console's map renderer reads `mapStyleUrl` in a separate change; until then the served value and the policy are in place, and map views keep the built-in style.
|`OS_BOOT_EMPTY`| flag |`0`| When `1`, boot the kernel with no metadata artifact (CLI internal). |
29
29
|`OS_MIGRATE_AND_EXIT`| flag |`0`| When `1`, run pending migrations and exit (suitable for one-shot jobs). |
30
30
|`OS_DISABLE_CONSOLE`| flag |`0`| When `1`, do not mount the Console portal under `/_console`. |
31
-
|`OS_CONSOLE_CSP_CONNECT_SRC`| list | — | Extra `connect-src` sources for the Console document's content-security-policy, separated by spaces: a scheme (`https:`) or a scheme with a host (`https://tiles.example.com`). Name the host of any map view `mapStyle` that is not the default demo style; the server itself, the default map style host and a configured `OS_TELEMETRY_CLIENT_ERROR_REPORTING_DSN` origin are already allowed. A malformed entry refuses the boot. |
31
+
|`OS_CONSOLE_CSP_CONNECT_SRC`| list | — | Extra `connect-src` sources for the Console document's content-security-policy, separated by spaces: a scheme (`https:`) or a scheme with a host (`https://tiles.example.com`). Name the host of any map view `mapStyle` that is not the default demo style, and the tile, sprite and glyph hosts of a style that keeps them elsewhere; the server itself, the default map style host, the `OS_CONSOLE_MAP_STYLE_URL` origin and a configured `OS_TELEMETRY_CLIENT_ERROR_REPORTING_DSN` origin are already allowed. A malformed entry refuses the boot. |
32
+
|`OS_CONSOLE_MAP_STYLE_URL`| URL | — | The deployment's default map style document (for example `https://tiles.example.com/styles/basic/style.json`). Served as `mapStyleUrl` on `GET /api/v1/runtime/config`, which is anonymous, so the URL is public; its origin joins the Console's `connect-src`. Unset, no `mapStyleUrl` is served. Must be an absolute `https:` URL on a host name, with no user name or password; anything else refuses the boot. |
32
33
|`OS_EAGER_SCHEMAS`| flag |`0`| When `1`, evaluate all lazily-built Zod spec schemas eagerly at module load instead of on first use — an emergency rollback of the lazy-schema memory optimization (higher startup RSS). |
33
34
|`OS_SKIP_SCHEMA_SYNC`| flag |`0`| When `1`, skip the implicit `db:sync` on boot. Use after running migrations manually. |
0 commit comments