Token gates follow the auto engine: no token ships, and an explicit engine beats an available token

The build config no longer bakes a Mapbox token, and the default engine is 'auto' (Mapbox only when a
token is available). Two gates still assumed the old contract.

- check:nested-token: with the build config's token empty (the shipped default), no leaf and no bundle
  may carry a pk. token, and every leaf must carry the runtime engine ladder (EXPLICIT / HAS_TOKEN),
  so with no token it draws MapLibre, keyless. A builder who bakes their own token gets the original
  check. Fails on the pre-rebuild tree, which still has baked tokens.
- check:provider-switch: the Mapbox baseline is now ?provider=mapbox with a fake host token (its
  requests are aborted, never sent); the switch run adds ?provider=maplibre to the same token (an
  explicit engine must win); a third run requires the no-token default to make no Mapbox requests.
This commit is contained in:
Doxa Maps 2026-09-26 03:37:53 +00:00
parent 0b9b068b44
commit be0a9efecf
2 changed files with 42 additions and 7 deletions

View file

@ -97,6 +97,16 @@ if (leaves.length < MIN_LEAVES) {
process.exit(1) process.exit(1)
} }
// ★ THE AUTO CONTRACT (2026-09-26, by project decision: no Mapbox token in the public repo). With the
// build config's "token" EMPTY — the shipped default — a leaf must carry NO token, and must instead carry
// the runtime engine ladder (EXPLICIT / HAS_TOKEN): with no token it draws MapLibre, keyless; with one
// from the host, ?tk=, a paste or a token service, Mapbox. A builder who opts into baking a token
// ("token": "pk.…" in their own config) gets the original check: every leaf carries it.
let bakedToken = ''
try { bakedToken = String(JSON.parse(readFileSync(join(ROOT, 'vite.doxa-maps-build-config.json'), 'utf8')).token || '') } catch { /* none */ }
const BAKED = TOKEN_RE.test(bakedToken)
const AUTO_RE = /var EXPLICIT = [^;]*;[\s\S]*?var HAS_TOKEN = /
const missing = [] const missing = []
let checked = 0 let checked = 0
for (const leaf of leaves) { for (const leaf of leaves) {
@ -108,9 +118,15 @@ for (const leaf of leaves) {
} }
checked++ checked++
const html = readFileSync(abs, 'utf8') const html = readFileSync(abs, 'utf8')
if (!TOKEN_RE.test(html)) { if (BAKED && !TOKEN_RE.test(html)) {
missing.push({ ...leaf, why: 'no pk. token baked into the leaf — a nested map here would get NOTHING' }) missing.push({ ...leaf, why: 'no pk. token baked into the leaf — a nested map here would get NOTHING' })
} }
if (!BAKED && TOKEN_RE.test(html)) {
missing.push({ ...leaf, why: 'a pk. token is in the leaf, but the build config bakes none — a token leaked into public output' })
}
if (!BAKED && !AUTO_RE.test(html)) {
missing.push({ ...leaf, why: 'no runtime engine ladder (EXPLICIT/HAS_TOKEN) — with no token this leaf cannot fall back to MapLibre' })
}
} }
// The other direction: a token must never be compiled into a .js bundle. // The other direction: a token must never be compiled into a .js bundle.
@ -127,7 +143,7 @@ console.log(`NESTED-TOKEN: ${checked} shipped leaf page(s) checked, ${(manifest.
if (missing.length || inBundles.length) { if (missing.length || inBundles.length) {
if (missing.length) { if (missing.length) {
console.error(`\nNESTED-TOKEN: FAIL — ${missing.length} leaf page(s) ship without a token:`) console.error(`\nNESTED-TOKEN: FAIL — ${missing.length} leaf page(s) break the token contract (${BAKED ? 'baked token' : 'auto, no token'}):`)
for (const m of missing.slice(0, 20)) console.error(` ✗ ${m.bundle}/${m.profile}\n ${m.url}\n ${m.why}`) for (const m of missing.slice(0, 20)) console.error(` ✗ ${m.bundle}/${m.profile}\n ${m.url}\n ${m.why}`)
if (missing.length > 20) console.error(` … +${missing.length - 20} more`) if (missing.length > 20) console.error(` … +${missing.length - 20} more`)
console.error( console.error(
@ -151,6 +167,7 @@ if (missing.length || inBundles.length) {
} }
console.log(`NESTED-TOKEN: tree public/${treeName}/ (name from ${treeNameSource}).`) console.log(`NESTED-TOKEN: tree public/${treeName}/ (name from ${treeNameSource}).`)
console.log('NESTED-TOKEN: PASS — every shipped leaf carries its own token, and no bundle does.') console.log(BAKED ? 'NESTED-TOKEN: PASS — every shipped leaf carries its own token, and no bundle does.'
: 'NESTED-TOKEN: PASS (auto) — no leaf or bundle carries a token, and every leaf picks its engine at runtime (MapLibre keyless without one).')
console.log(' This is DELIVERY only. It does not prove a basemap paints: that needs a real GPU and') console.log(' This is DELIVERY only. It does not prove a basemap paints: that needs a real GPU and')
console.log(' a token whose URL restrictions allow the serving host (see verify:map-paint).') console.log(' a token whose URL restrictions allow the serving host (see verify:map-paint).')

View file

@ -78,9 +78,17 @@ let OWN_SERVER = null
} }
} }
async function run(provider) { // A well-formed FAKE public token (never a real one): enough for the Mapbox engine to start and make its
// requests, which is all the comparison run needs. Requests that carry it are aborted, never sent.
const FAKE_TK = ['pk', Buffer.from(JSON.stringify({ u: 'provider-switch-check' })).toString('base64url'), 'c2ln'].join('.')
async function run(provider, token = '') {
const b = await chromium.launch({ args: ['--use-gl=swiftshader', '--enable-unsafe-swiftshader'] }) const b = await chromium.launch({ args: ['--use-gl=swiftshader', '--enable-unsafe-swiftshader'] })
const pg = await b.newPage({ viewport: { width: 1280, height: 900 } }) const pg = await b.newPage({ viewport: { width: 1280, height: 900 } })
if (token) {
await pg.addInitScript((tk) => { window.MAP_TOKEN = tk }, token)
await pg.route((u) => /(^|\.)mapbox\.com$/i.test(u.hostname) && u.searchParams.has('access_token'), (r) => r.abort())
}
const hosts = new Map() const hosts = new Map()
let unparsed = 0 let unparsed = 0
pg.on('request', (r) => { pg.on('request', (r) => {
@ -107,8 +115,13 @@ async function run(provider) {
return { mapboxReq: count('mapbox.com'), globals, errs, unparsed, total: [...hosts.values()].reduce((a, n) => a + n, 0) } return { mapboxReq: count('mapbox.com'), globals, errs, unparsed, total: [...hosts.values()].reduce((a, n) => a + n, 0) }
} }
const def = await run(null) // ★ THE AUTO CONTRACT (2026-09-26). The default engine is 'auto': Mapbox only when a token is available.
const ml = await run('maplibre') // So the Mapbox baseline is ?provider=mapbox WITH a (fake) host token, the switch run adds the same token
// and ?provider=maplibre (an explicit engine must beat an available token), and a third run checks the
// no-token default is already keyless.
const def = await run('mapbox', FAKE_TK)
const ml = await run('maplibre', FAKE_TK)
const auto = await run(null)
// DERIVED from the leaf's own HTML, not a hardcoded number: count the distinct // DERIVED from the leaf's own HTML, not a hardcoded number: count the distinct
// api.mapbox.com references the generated page carries, then allow a little slack // api.mapbox.com references the generated page carries, then allow a little slack
@ -120,8 +133,10 @@ const declared = new Set((pageHtml.match(/https:\/\/api\.mapbox\.com[^"')]+/g) |
const PRELOAD_ALLOWANCE = Number(process.env.PROVIDER_PRELOADS ?? (declared.size + 1)) const PRELOAD_ALLOWANCE = Number(process.env.PROVIDER_PRELOADS ?? (declared.size + 1))
console.log(` page preloads : ${declared.size} distinct api.mapbox.com url(s) in the leaf HTML`) console.log(` page preloads : ${declared.size} distinct api.mapbox.com url(s) in the leaf HTML`)
console.log(` default : ${def.mapboxReq} mapbox / ${def.total} total request(s) ` console.log(` ?provider=mapbox : ${def.mapboxReq} mapbox / ${def.total} total request(s) `
+ `[mapboxgl=${def.globals.mapbox} maplibregl=${def.globals.maplibre}]`) + `[mapboxgl=${def.globals.mapbox} maplibregl=${def.globals.maplibre}]`)
console.log(` no token (auto) : ${auto.mapboxReq} mapbox / ${auto.total} total request(s) `
+ `[mapboxgl=${auto.globals.mapbox} maplibregl=${auto.globals.maplibre}]`)
console.log(` ?provider=maplibre: ${ml.mapboxReq} mapbox / ${ml.total} total request(s) ` console.log(` ?provider=maplibre: ${ml.mapboxReq} mapbox / ${ml.total} total request(s) `
+ `[mapboxgl=${ml.globals.mapbox} maplibregl=${ml.globals.maplibre}]`) + `[mapboxgl=${ml.globals.mapbox} maplibregl=${ml.globals.maplibre}]`)
@ -144,6 +159,9 @@ if (def.mapboxReq <= PRELOAD_ALLOWANCE) {
fails.push(`the default run made only ${def.mapboxReq} mapbox request(s) — the map is probably ` fails.push(`the default run made only ${def.mapboxReq} mapbox request(s) — the map is probably `
+ 'not rendering, so this comparison proves nothing') + 'not rendering, so this comparison proves nothing')
} }
if (auto.mapboxReq > PRELOAD_ALLOWANCE) {
fails.push(`the no-token default made ${auto.mapboxReq} mapbox request(s) — 'auto' must draw MapLibre, keyless, with no token`)
}
if (!def.total) fails.push('no requests were recorded at all — the tally is broken, not the page') if (!def.total) fails.push('no requests were recorded at all — the tally is broken, not the page')
if (ml.errs.length) fails.push(`${ml.errs.length} page error(s) under maplibre: ${ml.errs[0]}`) if (ml.errs.length) fails.push(`${ml.errs.length} page error(s) under maplibre: ${ml.errs[0]}`)