Skip to content

Scaffold Nyuchi Maps: PMTiles-over-R2 Worker, styles, credits, docs, CI - #1

Merged
Bryan Fawcett (bryanfawcett) merged 4 commits into
stagingfrom
feat/scaffold-worker
Oct 16, 2026
Merged

Bryan Fawcett (bryanfawcett) merged 4 commits into
stagingfrom
feat/scaffold-worker

Conversation

@bryanfawcett

Copy link
Copy Markdown
Member

What

First slice of Nyuchi Maps, the platform base-map service (plan: mukoko-dev/mukoko-weather#298, owner decisions in its comments). Draft; not for merge until the owner reviews.

  • Worker (TypeScript, Hono) serving a PMTiles v3 archive from an R2 binding with range reads (pmtiles 4.5.0, BSD-3-Clause, pinned exact).
  • Routes: /health, /openapi.json, /v1/tiles.json, /v1/tiles/{z}/{x}/{y}.mvt, /v1/styles/{light,dark}.json, /v1/fonts/{stack}/{range}.pbf, /v1/sprites/{name}[@2x].{json,png}.
  • Long edge caching, ETag/304, 204 for empty in-range tiles, 404 out of range, strict validation (no path traversal), per-IP rate limit via RATE_LIMITER, CORS allow-list from ALLOWED_ORIGINS.
  • src/credits.ts is the single source of attribution; a test checks no credit repeats and that styles carry none (the TileJSON is the one source).
  • OpenAPI 3.1 (with TileJSON schema), README (data licences), NOTICE, docs/ARCHITECTURE.md, scripts/build-extract.md plus a regions file (documentation only; not run).
  • Host: PUBLIC_BASE_URL = https://maps.nyuchi.com (owner-confirmed), still a var.
  • CI mirrors mukoko-api (test, typecheck, build dry run, actions pinned to SHAs) plus the org lint caller.

Not done / not created

No R2 bucket, DNS record, route, secret or deploy. wrangler.jsonc: placeholder bucket name, route commented out, workers_dev: false, observability on. Keys for outside developers (nyk_/nys_ via api.nyuchi.com) are documented, not built. Protomaps credit is a marked TODO.

Not verified

Tested only against small in-memory PMTiles archives and a stubbed R2; not against real Protomaps data, a real bucket, or styles rendered in MapLibre.

Refs mukoko-dev/mukoko-weather#298

🤖 Generated with Claude Code

https://claude.ai/code/session_01WnTa759tUrAwMiSoif5g6w


Generated by Claude Code

First slice of the Nyuchi platform base-map service (mukoko-dev/mukoko-weather#298):
a TypeScript Cloudflare Worker (Hono) serving a PMTiles v3 archive from an R2
binding with range reads; TileJSON, light/dark styles, glyphs and sprites;
CORS allow-list, per-IP rate limiting, strict input validation; one credits
module; OpenAPI 3.1; NOTICE, architecture doc and an extract how-to; Workers
Vitest suite against a stubbed R2; CI mirroring mukoko-api plus the org lint.

Nothing is created or deployed: bucket name is a placeholder, the
maps.nyuchi.com route is commented out, workers_dev is off, no secrets.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnTa759tUrAwMiSoif5g6w
@bryanfawcett
Bryan Fawcett (bryanfawcett) changed the base branch from main to staging October 11, 2026 14:16
…ce design

- package.json / OpenAPI version 0.1.0 (release version policy).
- Add the mukoko-api overrides (sharp ^0.35.5, undici ^7.29.1): npm audit and
  dependency-review now clean.
- .npmrc: min-release-age=7 (Semgrep npm-missing-minimum-release-age).
- Tile sources registry (src/sources.ts) and createApp(sources): further PMTiles
  archives are served at /v1/sources/{id}/...; the base map stays the default.
  No overlay is built.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnTa759tUrAwMiSoif5g6w
The PR targets staging, where the release version check allows the next patch
(0.0.1); the minor bump belongs to the later staging to main release PR.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnTa759tUrAwMiSoif5g6w
…mits, licences; add deploy workflow

Blockers
- Tile responses (origin, CORS rebuild, cache-hit, and the Cache API copy) set
  encodeBody "manual" so gzip tiles are not compressed twice; a real-runtime
  test (SELF) proves one gzip layer, on a miss, with CORS, and on a cache hit.
- Use pmtiles' ResolvedValueCache (not SharedPromiseCache): a missing archive or
  a transient R2 error is never cached. Tests: 503 then 200 once uploaded; 500
  then 200.

Should-fix
- jsonError no longer overwrites a caller's Cache-Control (404s keep CACHE_MISS).
- Edge cache key is origin + path + TILES_VERSION (query strings cannot bypass
  it; bumping the var retires a replaced archive).
- Vary: Origin on every response.
- Rate limit: tiles counted only on an edge-cache miss; no-IP requests not
  limited; 1800/min; namespace_id 7402 (1001 is the Mukoko API's).
- TileJSON vector_layers from the archive metadata.
- Licences: OFL text and fonts/OFL.txt in the bucket layout, Devanagari stack,
  sprite licence still CONFIRM, Protomaps credit TODO records OSM-only
  attribution, one term (Produced Work).

Nits: If-None-Match answered before R2 reads; archive caches renamed; styles
memoised; preview_urls false; localhost moved to .dev.vars.example; reason for
legacy-peer-deps; persist-credentials false; OpenAPI and architecture fixes;
stricter dot-segment tests.

Deploy workflow mirrors mukoko-api (main push and manual, verify first,
production environment, wrangler-action pinned by SHA, smoke test /health).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnTa759tUrAwMiSoif5g6w
@bryanfawcett
Bryan Fawcett (bryanfawcett) marked this pull request as ready for review October 16, 2026 23:35
@bryanfawcett
Bryan Fawcett (bryanfawcett) merged commit 3120003 into staging Oct 16, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants