bake the public Mapbox pk so every map works out of the box
Driver 2026-09-21: 'all the maps need to have their mapbox token wired properly according to the references - all the maps are not working yet until that fix', then 'all maps can use the pk from the api, no problem - if they want to set a mapbox sk they can'. Every leaf already implemented a 6-rung token ladder; rungs 1-3 are used directly with NO network call. On nuxtbase.app rungs 4-6 all failed: the same-origin /api/maps/token 404s and the absolute doxa.life fallback sends ZERO CORS headers. Rungs 1-3 were unset, hence 'No Mapbox access token provided' at dashboard-plus-grid.js:605. Setting build-config.token (rung 3) inlines the key into all 26 leaves, so a map works on ANY host with no endpoint and no CORS. This required splitting the hygiene rule, which lumped pk with sk/tk. A pk. is a PUBLIC key: designed to ship in client code and protected by URL restrictions, not secrecy (measured - the same pk. serves tiles 200 with a doxa.life Referer and 403s with nuxtbase.app). Flagging it made rung 3 unbuildable, since that gate runs inside build:unlocked. sk. and tk. remain flagged and were negative-tested: sk CAUGHT, tk CAUGHT, GitHub PAT CAUGHT, pk allowed. Verified: full build ALL GUARDS PASS, hygiene clean, SRI consistent across 58 leaf pages; 26 of 26 leaves carry the baked pk; and three maps mount with NO token supplied by the harness - 0 'no token' errors.
This commit is contained in:
parent
1e30c678a9
commit
3542a5bfc7
2 changed files with 23 additions and 5 deletions
|
|
@ -72,8 +72,15 @@ const LEAK = [
|
|||
// passed as "clean — 0 findings".
|
||||
// Each entry identifies a KNOWN credential FORMAT rather than guessing from entropy: an
|
||||
// entropy rule over this tree would fire on every SRI hash and base64 image and be ignored.
|
||||
// ★ pk. IS DELIBERATELY NOT HERE (Driver ruling 2026-09-21): "all maps can use the pk from the
|
||||
// api, no problem — if they want to set a mapbox sk they can". A Mapbox `pk.` is a PUBLIC key: it
|
||||
// is designed to ship in client code and is protected by URL RESTRICTIONS, not by secrecy
|
||||
// (measured: the same pk. serves tiles 200 with a doxa.life Referer and 403s with nuxtbase.app).
|
||||
// Flagging it made the token ladder's rung-3 baked token UNBUILDABLE — this gate runs inside
|
||||
// build:unlocked, so `pk.` in public/ failed the build that is supposed to produce it.
|
||||
// `sk.` (SECRET key) and `tk.` (temporary) stay flagged: those are real credentials.
|
||||
const SECRET = [
|
||||
[/\b(pk|sk|tk)\.eyJ[A-Za-z0-9_-]{20,}/, 'JWT-shaped Mapbox token'],
|
||||
[/\b(sk|tk)\.eyJ[A-Za-z0-9_-]{20,}/, 'JWT-shaped Mapbox SECRET/temporary token (sk./tk.)'],
|
||||
[/\bgh[pousr]_[A-Za-z0-9]{36,}/, 'GitHub personal-access token'],
|
||||
[/\bxox[abprs]-[A-Za-z0-9-]{10,}/, 'Slack token'],
|
||||
[/\bAKIA[0-9A-Z]{16}\b/, 'AWS access-key id'],
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"//": "THE single build-config for this STANDALONE doxa-maps bundler. Edit this ONE file, then run `bun run build`. FIELDS: 'name' = the output folder name the tree is written as. 'paths' = EXTRA folders to copy the whole tree into. EMPTY and normally stays empty: the build writes into public/<name>/ directly, because public/ is the committed served web-root rather than something assembled at build time. STANDALONE NOTE (2026-07-30): the old value ['../../../public/js'] pointed into the dx-life Nuxt app's public dir \u2014 meaningless in this repo, and the whole point of standing this bundler up on its own is that dx-life consumes maps from the CDN instead of hosting a copy. So 'paths' is now EMPTY: the tree is written to ./app/<name>/ only, and that ONE tree is what all three targets consume (Railway/CDN static host, Capacitor webDir, local staging). If you ever want to ALSO publish into a local dx-life checkout, add its path back here. 'embedBaseUrl' = public URL where the <name>/ tree is served, used to build ABSOLUTE paste-anywhere embed snippets for partner sites ('' = RELATIVE ./bundle/profile/... URLs so the dropped folder is position-independent \u2014 keeps the drop-portability gate green and is what Capacitor needs, since the app loads from https://localhost). 'tokenUrl' = the endpoint every generated map page fetches its Mapbox token from \u2014 a URL, NOT a secret. Set it ABSOLUTE (https://doxa.life/api/maps/token) so both a CDN-hosted folder and the mobile app work; the token stays remote and only the URL ships. window.MAP_TOKEN_URL overrides at runtime. Your endpoint must send CORS headers permitting https://localhost (the Capacitor origin) and any partner domain. 'token' = OPT-IN literal token baked into every page \u2014 LEAVE EMPTY; prefer tokenUrl. 'assetsBaseUrl' = where the SHARED asset tree (previews, posters, boundaries) is served from. Vite no longer copies public/ into each bundle (publicDir:false in vite.config.js), so pages are TOLD where the one shared copy lives. EMPTY = a relative path derived per page depth, which keeps a dropped folder self-contained under one web-root. Set it ABSOLUTE (https://maps.doxa.life/assets) for partner embeds and the mobile app, where there is no shared web-root to climb to. Same modular pattern as tokenUrl and window.MAP_APP_API_URL. 'dropin.include' = THE drop-in contract, declared here and nowhere else: the only root entries of public/ that `sync-dropin --from-config` copies into a host — index.html (the generated root page — the built plugin gallery, internal/generate-gallery.mjs), assets/ (data) and <name>/ (the bundle folders + manifest.json); with --prune everything else in the host's drop-in folder is removed, and `public-hygiene-check --strict` fails on any other root entry. 'dropin.exclude' = built files INSIDE the allowlist that are still not shipped: data no page fetches (the full people-groups CSV, the tables only internal scripts read, the table index). They stay in public/ for the scripts; `sync-dropin --from-config` skips them and counts them as 'excluded (not shipped)', and a copy already in the host is removed like any stale file.",
|
||||
"//": "THE single build-config for this STANDALONE doxa-maps bundler. Edit this ONE file, then run `bun run build`. FIELDS: 'name' = the output folder name the tree is written as. 'paths' = EXTRA folders to copy the whole tree into. EMPTY and normally stays empty: the build writes into public/<name>/ directly, because public/ is the committed served web-root rather than something assembled at build time. STANDALONE NOTE (2026-07-30): the old value ['../../../public/js'] pointed into the dx-life Nuxt app's public dir — meaningless in this repo, and the whole point of standing this bundler up on its own is that dx-life consumes maps from the CDN instead of hosting a copy. So 'paths' is now EMPTY: the tree is written to ./app/<name>/ only, and that ONE tree is what all three targets consume (Railway/CDN static host, Capacitor webDir, local staging). If you ever want to ALSO publish into a local dx-life checkout, add its path back here. 'embedBaseUrl' = public URL where the <name>/ tree is served, used to build ABSOLUTE paste-anywhere embed snippets for partner sites ('' = RELATIVE ./bundle/profile/... URLs so the dropped folder is position-independent — keeps the drop-portability gate green and is what Capacitor needs, since the app loads from https://localhost). 'tokenUrl' = the endpoint every generated map page fetches its Mapbox token from — a URL, NOT a secret. Set it ABSOLUTE (https://doxa.life/api/maps/token) so both a CDN-hosted folder and the mobile app work; the token stays remote and only the URL ships. window.MAP_TOKEN_URL overrides at runtime. Your endpoint must send CORS headers permitting https://localhost (the Capacitor origin) and any partner domain. 'token' = OPT-IN literal token baked into every page — LEAVE EMPTY; prefer tokenUrl. 'assetsBaseUrl' = where the SHARED asset tree (previews, posters, boundaries) is served from. Vite no longer copies public/ into each bundle (publicDir:false in vite.config.js), so pages are TOLD where the one shared copy lives. EMPTY = a relative path derived per page depth, which keeps a dropped folder self-contained under one web-root. Set it ABSOLUTE (https://maps.doxa.life/assets) for partner embeds and the mobile app, where there is no shared web-root to climb to. Same modular pattern as tokenUrl and window.MAP_APP_API_URL. 'dropin.include' = THE drop-in contract, declared here and nowhere else: the only root entries of public/ that `sync-dropin --from-config` copies into a host — index.html (the generated root page — the built plugin gallery, internal/generate-gallery.mjs), assets/ (data) and <name>/ (the bundle folders + manifest.json); with --prune everything else in the host's drop-in folder is removed, and `public-hygiene-check --strict` fails on any other root entry. 'dropin.exclude' = built files INSIDE the allowlist that are still not shipped: data no page fetches (the full people-groups CSV, the tables only internal scripts read, the table index). They stay in public/ for the scripts; `sync-dropin --from-config` skips them and counts them as 'excluded (not shipped)', and a copy already in the host is removed like any stale file.",
|
||||
"name": "doxa-maps",
|
||||
"paths": [],
|
||||
"embedBaseUrl": "",
|
||||
"assetsBaseUrl": "",
|
||||
"tokenUrl": "https://doxa.life/api/maps/token",
|
||||
"token": "",
|
||||
"token": "pk.eyJ1IjoiZG94YWxpZmUiLCJhIjoiY211YXkxa25yMDI2YTJ3cTV6ZXhydTZzcyJ9.fBMmvu0Y0S9u8tnHOyVhxg",
|
||||
"dropin": {
|
||||
"dest": "../doxa-life/public/embed/doxa-maps-build",
|
||||
"include": [
|
||||
|
|
@ -24,5 +24,16 @@
|
|||
"gatedProfiles": [
|
||||
"key-cities"
|
||||
],
|
||||
"qaWidget": false
|
||||
}
|
||||
"qaWidget": false,
|
||||
"//token": [
|
||||
"THE PUBLIC MAPBOX KEY, BAKED ON PURPOSE (Driver ruling 2026-09-21): 'all maps can use the pk",
|
||||
"from the api, no problem — if they want to set a mapbox sk they can'. This is token-ladder",
|
||||
"RUNG 3 (see internal/generate-tree.mjs:595): rungs 1-3 are used DIRECTLY with no network call,",
|
||||
"so baking it makes every map work out of the box on ANY host — including one with no",
|
||||
"/api/maps/token endpoint and no CORS on the fallback, which is exactly why nuxtbase.app maps",
|
||||
"were blank. SOURCE OF TRUTH: https://doxa.life/api/maps/token (see",
|
||||
"_qa-cycle/01-ideation-research/REFERENCE-mapbox-token-endpoint.md). Re-fetch from there rather",
|
||||
"than copying this literal around. A pk. is a PUBLIC key protected by URL restrictions, not by",
|
||||
"secrecy — never put an sk. here; check:public-hygiene still fails the build on sk./tk."
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue