pgx
AI 에이전트를 위한 경량 Postgres, Rust로 구현, 하나의 런타임에서 수천 개의 격리된 데이터베이스 제공
레거시 제약을 넘어 자체 호스팅 파워로 전환. 누구든 어디서든 복제 가능, 데이터와 규칙은 직접 관리
From legacy limits to self-hosted power. Anyone can replicate. Anywhere you run it. Your data, your rules. Your city, your future.
데이터TypeScript · CSSGitHub @Nonarkara온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Bangkok civic intelligence hub — live PM2.5, satellite, alerts. Independent of any vendor platform.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Bangkok civic intelligence / smart-city OS — independent of vendors. Live air, water, traffic, satellite, civic reports, and a bilingual morning brief on one screen. This is not an official product of BMA, DEPA, or any government. It is independent civic software by Non Arkaraprasertkul. Fork it. Run it on one Mac. เมืองที่เข้าใจเรา และเราเข้าใจเมือง — the city that understands us, and we understand the city.README 전문 보기 ↗
Bangkok civic intelligence / smart-city OS — independent of vendors. Live air, water, traffic, satellite, civic reports, and a bilingual morning brief on one screen.
This is not an official product of BMA, DEPA, or any government. It is independent civic software by Non Arkaraprasertkul. Fork it. Run it on one Mac.
เมืองที่เข้าใจเรา และเราเข้าใจเมือง — the city that understands us, and we understand the city.
City Hub is a replicable civic intelligence and planning tool. Bangkok is the full-tier city (tier: 'full' in src/config/cities.ts). Other cities in that same registry — Chiang Mai, Phuket, Singapore, Kuching, Kranj, Ljubljana, and more — run on global open sources (tier: 'lite'). Adding a city is one config object; pointing the map at a typed name uses OpenStreetMap Nominatim for a session-only view.
It exists so a resident, journalist, or city operator can open one tab and see what the sensors say next to what the press says, with every figure cited. It is not a brochure dashboard. Layers that are pending, historical, mocked, or key-gated say so in src/config/bangkok-layers.ts.
Since v7 the top bar carries three actions — Compare, Look, Research —
and everything else lives under More. The line under the title is the finding
for the current city; clicking it opens the argument. More → Plan models the
city as eight systems with capacity and demand, says what to do about the ones
going negative, and lists the assumptions that would change the ranking. Every
coefficient on that panel is marked MEASURED, DERIVED or ASSUMED.
Reading the code cold? Start at SYSTEM.md — how the layers fit, what the data contracts are, and why the OSM traffic goes through a proxy.
The product name in the UI is Dr Non's City Hub. package.json version is 0.9.4. GitHub description: Bangkok civic intelligence hub — live PM2.5, satellite, alerts. Independent of any vendor platform.
Drawn from the layer catalog and data modules — not from marketing copy:
| Domain | Sources in this repo |
|---|---|
| Air | GISTDA PM2.5, Air4Thai (PCD), Open-Meteo AQI, WAQI, OpenAQ |
| Water / flood | GISTDA live + historical flood polygons, Thaiwater quality and levels |
| Fire | NASA FIRMS (needs Worker FIRMS_MAP_KEY or VITE_FIRMS_MAP_KEY); GISTDA hotspots are a historical 2023 snapshot, not live |
| Traffic | Longdo / iTIC live tiles + incidents (default-on); TomTom flow/incidents if VITE_TOMTOM_KEY is set |
| Civic | Traffy Fondue citizen reports (points + heatmap) |
| Weather | TMD 7-day official forecast (Thai + English); Open-Meteo |
| Satellite | NASA GIBS (true color, night lights, LST, NDVI, aerosol, …), ESRI World Imagery, EOX Sentinel-2 cloudless; AlphaEarth / Sentinel-5P / GHSL need Earth Engine Worker secrets |
| 3D twin | OSM footprints baked to GeoJSON (public/geo/bkk-buildings.geojson, ODbL); heights from OSM, not invented |
| Narrative | GDELT headlines + sentiment; Reality Check verdict (CALM / UNDERSTATED / CONFIRMED / OVERSTATED) vs sensors |
| Brief | Governor-style panel; optional Gemini narration via the Worker; optional local Ollama ASK chat |
Transit rail geometry is a hand-curated BTS/MRT/ARL GeoJSON. The gtfs-transit-live layer is documented in the catalog as a mock GTFS-RT simulation — not a live operator feed.
Five tenets this public README is written to:
wrangler dev. ASK chat defaults to Ollama on your machine (http://localhost:11434) — nothing leaves it unless you turn on the cloud toggle. You do not need a vendor platform, a cluster, or a city IT department to try this.src/config/cities.ts is the replicability layer. scripts/bake-city-buildings.py ports the 3D path (Bangkok, then Kranj, then Ljubljana). MIT, as stated in this file and STORY.md.nameLocal (กรุงเทพฯ, เชียงใหม่, ภูเก็ต, ยะลา), IBM Plex Sans Thai, TMD descriptions in Thai and English, iTIC incident labels in Thai, UI aphorism ทุกอย่างเกิดขึ้นเพราะมีเหตุ. English for the rest of the operator chrome.BangkokIntelligence are cited. Empty, stale, pending, and mocked layers are labeled. The dashboard would rather show a gap than invent a number.STORY.md, not an official endorsement.scripts/bake-city-buildings.py: no invented metrics). Twin KPIs that are curated say so and name a source. Forecast backends tell the truth: the Worker’s /forecast model field reports Gemini, TimeFM, or Holt-Winters — whichever actually answered..env.example to .env.local. Worker secrets go through wrangler secret put. Never commit .env.local. This README does not include tokens, service-account JSON, or demo passwords.worker/src/index.ts origin-locks CORS to this project’s Pages hosts, *.nonarkara.org, and localhost. Do not strip that allowlist and republish it as an open CORS relay.flowchart TB
subgraph mac [One Mac]
Dev["npm run dev — Vite + React 19"]
Ollama["Optional: Ollama ASK chat\nlocalhost:11434"]
WDev["Optional: wrangler dev :8787"]
end
subgraph spa [Browser SPA]
UI["Panels, bilingual brief, layer rail"]
Map["MapLibre GL\noptional Mapbox vector tiles\nelse NASA / ESRI / Sentinel rasters"]
end
subgraph edge [Cloudflare]
Pages["Pages — static dist/"]
Worker["Worker cityhub-proxy\nCORS + key injection\n/forecast /narrate /ee/mapid"]
end
subgraph open [Public APIs — cited, replaceable]
Direct["CORS-friendly: Open-Meteo, USGS, NASA GIBS, OSM"]
Proxied["Often proxied: FIRMS, Traffy, GDELT,\nAir4Thai, TMD, data.go.th, WAQI, OpenAQ"]
end
Dev --> spa
Pages --> spa
UI --> Map
UI --> Direct
UI --> Ollama
UI --> Worker
UI --> WDev
Worker --> Proxied
WDev --> Proxied
Stack (from package.json and source, not the old UNL notes):
maplibre-gl. A Mapbox public pk.* token unlocks dark-vector and satellite-streets basemaps; without it the map still has NASA GIBS / ESRI / Sentinel-2 rasters)worker/ for CORS-blocked APIs and optional Gemini / TimeFM / Earth Enginesrc/config/cities.tsThe Vite alias @shared → ../_shared is used by src/data/gistda.ts for GISTDA helpers. That sibling folder is not in this repository. A clone of only city-hub will not resolve those imports until _shared/lib/gistda.js exists next to the checkout (or you change the alias). Other layers do not go through that file.
Requires Node.js and npm (lockfile is npm).
git clone https://github.com/Nonarkara/city-hub.git
cd city-hub
npm install
cp .env.example .env.local
npm run dev
Vite serves the SPA (typically http://localhost:5173). Bangkok super-mode is the default city in CITIES.
.env.example is the env contract. Names only:
| Variable | Role |
|---|---|
VITE_MAPBOX_ACCESS_TOKEN | Optional. Public pk.* token; URL-restrict it to your Pages hosts. Unlocks Mapbox vector / satellite-streets. |
VITE_PROXY_URL | Cloudflare Worker origin. Many fetchers use it when set; some (OpenWeatherMap, OpenAQ, narrate) fall back to http://127.0.0.1:8787. |
VITE_WAQI_TOKEN | Optional WAQI token |
VITE_LONGDO_KEY | Optional Longdo key |
VITE_TOMTOM_KEY | Optional TomTom (free-tier note in .env.example: 2,500 req/day) |
VITE_OPENWEATHERMAP_KEY | Optional OpenWeatherMap One Call 3.0 |
VITE_OPENAQ_KEY | Optional OpenAQ v3 |
VITE_FIRMS_MAP_KEY | Optional; direct FIRMS in dev. Production should use the Worker secret instead |
VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY | Optional cache |
VITE_OLLAMA_URL / VITE_OLLAMA_MODEL | Optional ASK chat. Code default model is phi4-mini (src/lib/ollama.ts); .env.example still shows qwen2.5-coder:1.5b as an override example |
VITE_GEMINI_MODEL | Optional cloud ASK via Firebase AI Logic; Firebase web config is in src/lib/firebase.ts (client identifiers, restricted by hostname in the Firebase console) |
Mapbox is not required to see a map. FIRMS, some keyed air/traffic APIs, Gemini narration, TimeFM, and AlphaEarth are silent or empty until the matching env/Worker secret exists — that is intentional.
From worker/README.md and worker/wrangler.toml (name = "cityhub-proxy"):
cd worker
npm install
npx wrangler dev # http://localhost:8787
Point the app at it:
# in .env.local
VITE_PROXY_URL=http://127.0.0.1:8787
Production secrets (names only — set with npx wrangler secret put SECRET_NAME inside worker/):
GEMINI_API_KEY, WAQI_TOKEN, OPENAQ_KEY, OWM_KEY, FIRMS_MAP_KEY, GCP_SERVICE_ACCOUNT_JSON, optional HF_API_TOKEN, TIMEFM_ENDPOINT_URL.
POST /forecast tries Gemini, then TimeFM, then in-Worker Holt-Winters. /narrate is Gemini. /ee/mapid is Earth Engine. Setup steps for TimeFM and AlphaEarth are in worker/README.md.
Documented deployed Worker hostname in context.md: cityhub-proxy.drnon.workers.dev (older unl-city-proxy.drnon.workers.dev is retired).
From .env.example and src/lib/ollama.ts:
# install https://ollama.com then e.g.
ollama pull phi4-mini && ollama serve
For the deployed site to call your Mac:
OLLAMA_ORIGINS="https://city-hub.pages.dev,https://hub.nonarkara.org" ollama serve
From scripts/bake-city-buildings.py and context.md (heights from OSM only):
python3 scripts/bake-city-buildings.py \
--city ljubljana \
--bbox 14.48,46.03,14.54,46.08 \
--output public/geo/ljubljana-buildings.geojson
Then set threeD: true and buildings3dUrl on that city in src/config/cities.ts.
From the previous README and context.md (Vite inlines VITE_* at build time):
npm run build
npx wrangler pages deploy dist --project-name city-hub --branch main \
--commit-hash=$(git rev-parse --short HEAD) \
--commit-message="$(git log -1 --pretty=%s)"
functions/ is bundled with the Pages deploy automatically — the Nominatim and
Overpass proxies ship with the static app, no separate deploy step.
Cloudflare Pages Git integration: framework preset Vite, build command npm run build, output dist. Set VITE_MAPBOX_ACCESS_TOKEN and VITE_PROXY_URL (and any other VITE_* you need) in Pages build environment variables for Production and Preview.
cd worker && npx wrangler deploy
Documented public host — also the GitHub repository homepage:
index.html and the About modal also name https://hub.nonarkara.org. Treat both as this project’s public fronts. This README does not claim uptime; if the SPA error boundary or the origin is down, run locally with the steps above.
| Doc | What it is |
|---|---|
| SYSTEM.md | How the system fits together — read this before changing a fetcher or a contract |
| docs/MSH.md | The layout law — when you find SH, massacre. Read before designing, building, or reviewing any interface |
| STORY.md | Why the UNL tiles were dropped; vendor independence |
| CONTRIBUTING.md | How to run a fork and what pull requests we want |
| SECURITY.md | How to report a vulnerability |
| LICENSE | MIT — GitHub-detectable |
| docs/DESIGN-AUDIT-LYNCH.md | The five Lynch lenses, and why OSM features are candidate evidence rather than a mental map |
| docs/IOC-2.0-RESEARCH.md | Research and design for Image of the City 2.0 — binding the eight city systems to Lynch's elements, with page references to the 1960 source |
| docs/hero-banner.png | Public README hero (this banner) |
| docs/UNL-DEPENDENCY-INVENTORY.md |
Released under the MIT License. Copyright © 2026 Non Arkaraprasertkul.
Use it, fork it, ship your own city’s version. Keep the copyright notice. This repository is independent civic software — not a vendor platform and not an official BMA, DEPA, or government product.
An in-app About panel currently prints a stricter “all rights reserved / written
licence” paragraph. The public repository license is MIT (LICENSE on
main). Do not take the About modal as overriding that file.
OpenStreetMap building extracts are ODbL. Upstream open-data providers keep their own terms.
See CONTRIBUTING.md and SECURITY.md.
Built by Non Arkaraprasertkul. ทุกอย่างเกิดขึ้นเพราะมีเหตุ.
| What UNL did vs what replaced it (historical; some env names in it are obsolete) |
| docs/IMPLEMENTATION-2026-05-26.md | Air4Thai, TMD seismic, Thaiwater, TomTom, Airbnb, correlation engine |
| docs/SESSION-2026-05-27-CIVIC-INTELLIGENCE.md | Insight stacks, city onboarding, per-city open data |
| worker/README.md | Proxy, forecast, narrate, Earth Engine |
| context.md | Internal engineering notes — some UNL-era; prefer package.json + source when they disagree |