vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
오픈소스 무키 실시간 전세계 모니터링, 항공·해양·위성·공공 카메라·위험·사이버와 데이터 출처·비행경로 계획 제공
Open-source, keyless, real-time global monitor: flights, maritime, satellites, public cameras, hazards and cyber — with honest data provenance and a flight path planner.
데이터TypeScript · HTMLGitHub @awne8886온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
GLOBAL INTELLIGENCE MONITOR — an open-source, keyless, real-time "god's-eye" view of the planet on a
WebGL globe: aircraft, maritime, satellites, public traffic cameras, hazards, conflict and cyber
indicators, markets and live alerts, plus a Flight Path Planner for the route between any two airports.
It runs with zero API keys. Keys only unlock upgrades. Every number on screen comes from a named
source, with the time it was observed and how fresh it is.README 전문 보기 ↗GLOBAL INTELLIGENCE MONITOR — an open-source, keyless, real-time "god's-eye" view of the planet on a WebGL globe: aircraft, maritime, satellites, public traffic cameras, hazards, conflict and cyber indicators, markets and live alerts, plus a Flight Path Planner for the route between any two airports.
It runs with zero API keys. Keys only unlock upgrades. Every number on screen comes from a named source, with the time it was observed and how fresh it is.
/docs (API reference), /privacy (what leaves the instance), /cameras-notice (camera
policy and takedown).GODSEYE replicates the feature set of an existing MIT-licensed monitor (credited under Licence below) and was built to answer the criticism that project received: synthetic data shown as live intelligence, docs that drifted from the API, and weak security and licensing. The rules are enforced in code and tests, not just stated:
Math.random is a lint error in
shipped code; retry-timing jitter in the HTTP client is the only exemption). Blocklist hits are drawn as INDICATOR points; attack arcs appear only when a source
reports both ends. Static layers (nuclear sites, chokepoints, conflict zones, ports) are badged
REFERENCE, never LIVE.observedAt and fetchedAt separately; LIVE
only appears for data observed within its cadence. A source that fails shows SOURCE OFFLINE with
its last-good time — the API answers 503 source_offline, never an empty list pretending to be current.{ok, count, ms, age_s}.src/lib/api-catalog.ts generates /docs, docs/API.md, the Privacy
page's list of third parties and the per-route rate limits; tests fail when they disagree.With earthquakes, fires, alert pins or GDELT events on, a strip at the bottom of the map scrubs back up to 24 h. It replays only observed times from those feeds (nothing interpolated; untimed items are left out; time a feed does not hold is greyed out), the HUD reads REPLAY hh:mm UTC instead of LIVE, and the day/night terminator follows the cursor. With the slider focused: ←/→ 15 min, Shift+←/→ or PgUp/PgDn 1 h, Home = 24 h ago, End or Esc = live. The cursor is not kept in the URL, so shared links open live.
Captured from a production build with live keyless feeds (software WebGL; see docs/screenshots for the full set and how to re-capture).
Requirements: Node.js ≥ 22.12 and pnpm 10 (via Corepack).
corepack enable
pnpm i
pnpm dev # http://localhost:3000
pnpm dev and pnpm build first vendor the MapLibre worker into public/maplibre/<version>/.
pnpm build && pnpm start serves a production build. The three web fonts (Inter, JetBrains Mono,
Space Grotesk; OFL) come from @fontsource packages through next/font/local, so the build needs no
font download and the browser never contacts a font CDN.
docker compose up -d # app + Caddy on https://localhost (Caddy's internal CA)
GODSEYE_DOMAIN=monitor.example.org docker compose up -d # automatic HTTPS for your domain
docker compose --profile redis up -d # add Redis (set REDIS_URL=redis://redis:6379 in .env)
The image (Dockerfile) is a multi-stage node:22-alpine build (base pinned by digest):
pnpm install --frozen-lockfile, pnpm build, then the standalone server with .next/static and
public/, running as uid 1001 without a login shell or package managers, with a HEALTHCHECK on
/api/health. No secrets are baked in: put keys in .env (read at run time by compose) or pass them as
environment variables. Compose runs every container with a read-only root filesystem,
no-new-privileges and all Linux capabilities dropped (Caddy keeps only NET_BIND_SERVICE; Redis runs
as its own user); the app writes only to the snapshots volume (feed snapshots survive restarts) and
two small tmpfs mounts. The app container is never published directly; Caddy (Caddyfile) terminates
TLS, overwrites X-Forwarded-For / X-Real-IP, caps request bodies (64 KB on /api/ai/*, 1 MB
elsewhere), compresses JSON and HTML with zstd/gzip, streams Server-Sent Events unbuffered and keeps no
access log. The Caddy and Redis images are pinned by digest too; refresh the digests with
docker buildx imagetools inspect caddy:2-alpine (or redis:8-alpine) when you update.
Every variable is optional; copy .env.example to .env and fill in only what you need.
GET /api/health shows which capabilities are on and, if not, why. Never put secrets in NEXT_PUBLIC_*
variables.
| Variable | Effect |
|---|---|
GODSEYE_CONTACT | Email or URL appended to the User-Agent sent to every upstream (default: this repo's issue tracker). Please set it on public instances. |
GODSEYE_DOMAIN | Public hostname for the bundled Caddy in docker compose (automatic HTTPS). |
COMMERCIAL_DEPLOYMENT | true turns off every non-commercial source (capabilities nc_sources, openmeteo, aeroapi, adsbfi, cloudflare, deepstate). |
NONCOMMERCIAL | true enables DeepStateMap frontlines (deepstate; never with COMMERCIAL_DEPLOYMENT). Non-commercial use with attribution only; DeepState requires prior approval for commercial use of its API. |
TRUSTED_PLATFORM | cloudflare, vercel or akamai: trust that edge's client-IP header. Leave empty behind your own proxy. |
TRUSTED_PROXY_HOPS | Number of proxies appending to X-Forwarded-For (default 1: the rightmost entry is the client). |
TRUST_PROXY_HEADER | Trust exactly one header your proxy overwrites, e.g. x-real-ip. |
HSTS_PRELOAD | true adds preload to HSTS (commits the whole domain to HTTPS). |
SSE_MAX_DURATION_MS | Close event streams after this many ms so capped hosts reconnect cleanly (Vercel default 280000). |
SSE_MAX_BUFFERED_BYTES | Process-wide budget for bytes queued to slow event-stream clients (default 256 MB); past it the slowest clients are dropped. |
GODSEYE_DISABLE_POLLER | true disables the background upstream poller (tests, static previews). |
| Variable | Capability / effect |
|---|---|
REDIS_URL | redis: shared snapshots, single-writer locks and rate limits across instances. |
SNAPSHOT_STORE | Force memory, filesystem or redis. |
SNAPSHOT_DIR | Filesystem snapshot store directory (survives restarts). |
SNAPSHOT_MEMORY_MAX_BYTES, SNAPSHOT_MAX_FILES, SNAPSHOT_MAX_DISK_BYTES | Bounds for per-query cache entries in the memory (default 256 MB) and filesystem (default 20000 files, 1 GB) stores; feed snapshots are never evicted. |
PHOTON_URL, NOMINATIM_URL | Self-hosted geocoders instead of the public demo servers. |
VALHALLA_URL, OSRM_URL | Self-hosted routing engines. |
ARCGIS_ALLOWED_HOSTS | Extra ArcGIS servers /api/arcgis may import from (exact hosts with an optional :port such as :6443, https only); ArcGIS Online hosted services (services.arcgis.com, services[1-9].arcgis.com, services-eu1/services-ap1.arcgis.com) and *.arcgisonline.com are built in. |
| Variable(s) | Capability | Unlocks |
|---|---|---|
ADSBLOL_REAPI=true | adsblol_reapi | adsb.lol re-api (only from a feeder IP) |
OPENSKY_CLIENT_ID, OPENSKY_CLIENT_SECRET, OPENSKY_LICENSED=true | opensky | OpenSky (requires a written licence for live products) |
ADSBFI_PERSONAL_USE=true | adsbfi | adsb.fi open data (personal, non-commercial use only) |
FPDB_API_KEY | fpdb | FlightPlanDatabase plans (flight simulation only) |
AEROAPI_KEY | aeroapi | FlightAware AeroAPI personal tier: filed IFR routes on a pair and flight schedules (non-commercial; off when COMMERCIAL_DEPLOYMENT=true) |
AIS_API_KEY | ais | Live AIS vessels via a server-side AISStream relay |
TFL_APP_KEY | tfl | TfL Unified API ("Powered by TfL Open Data") |
CCTV_LINK_OUT_ONLY | — | Region keys or country codes whose cameras are shown as operator links only (no previews) |
CCTV_REMOVED_IDS | — | Camera ids removed on request (report/remove button), comma-separated |
TRAFIKVERKET_KEY | trafikverket | Trafikverket API (stills are keyless) |
CLOUDFLARE_API_TOKEN | cloudflare | Cloudflare Radar outages and attack origins (CC BY-NC data) |
ABUSECH_AUTH_KEY | abusech | abuse.ch APIs (bulk files stay keyless) |
NVD_API_KEY | nvd | Higher NVD rate limit |
OTX_KEY | otx | AlienVault OTX |
SDK_INGEST_KEY | sdk | GODSEYE SDK entity ingest (fail-closed without a key) |
Keyless licence gates that are on by default: nc_sources (TeleGeography cables, OpenSanctions bulk,
abuse.ch, ip-api, Shodan InternetDB, City of Edmonton cameras) and openmeteo (Open-Meteo free tier);
both are off when COMMERCIAL_DEPLOYMENT=true, as are the keyed aeroapi and cloudflare.
X-Forwarded-For. Rate limits are per
route and per client IP; if clients can supply their own X-Forwarded-For, they choose their own
bucket. Caddy: header_up X-Forwarded-For {remote_host} (as in Caddyfile); nginx:
proxy_set_header X-Forwarded-For $remote_addr;. Never expose next start or node server.js
directly: without a proxy that overwrites the header, every forged X-Forwarded-For value gets a
fresh bucket, which in effect disables rate limiting (the server logs a warning at start-up when no
trusted proxy is configured). Behind Cloudflare, Vercel or Akamai set TRUSTED_PLATFORM instead.text/event-stream (the app also sends
X-Accel-Buffering: no) and keep read timeouts above the 15 s heartbeat.REDIS_URL so they share snapshots, the single-writer locks and the rate
limits; otherwise each instance polls every upstream itself.SSE_MAX_DURATION_MS, 280 s by
default there, and the browser reconnects), egress IPs are shared, so per-IP quotas such as CelesTrak
and Nominatim behave worse, and the Hobby plan is non-commercial. Use one region and a Redis
(for example Upstash) REDIS_URL. vercel.json pins one region and the 300 s function ceiling
(Fluid compute); output, the SSE cap and the trusted client-IP header switch automatically
when VERCEL is set, so no extra environment variables are required.X-Frame-Options, nosniff, Permissions-Policy) are set by the
app itself; see docs/ARCHITECTURE.md for the trade-offs./privacy, and /api/geo sends the visitor's IP to a geolocation provider only after
they click "centre on my region". /privacy also lists every third party that receives something a
visitor typed, generated from the endpoint catalogue./cameras-notice.pnpm lint # ESLint 10, zero warnings
pnpm typecheck # TypeScript strict
pnpm test # Vitest (pnpm test:coverage enforces ≥ 80 % lines on src/lib, src/app/api, src/features/flight-paths)
pnpm build
pnpm e2e # Playwright (starts the production server itself)
After changing the endpoint catalogue or a probe log, regenerate the generated docs (tests fail when they are stale):
node --experimental-transform-types --import ./tools/ts-loader.mjs tools/gen-api-docs.ts
node --experimental-transform-types --import ./tools/ts-loader.mjs tools/compile-data-sources.ts
Probe every new upstream with curl and the GODSEYE User-Agent before wiring it, and record status,
latency, CORS, auth and licence in docs/data-sources/<area>.md. Project rules for contributors and
coding agents are in CLAUDE.md. CI runs all of the above plus pnpm audit --prod and
Lighthouse CI on every push and pull request, both on ubuntu-24.04: /docs and /privacy against
the contract thresholds (pnpm lhci, lighthouserc.json), and / against a separate software-GL
budget (pnpm lhci:home, lighthouserc.home.json), described in the next section. A GPU runner, if
you provide one, adds the measurement of / against the contract thresholds.
// is the WebGL globe. GitHub's standard runners have no GPU, so in CI Chromium draws the globe with
SwiftShader, a software rasteriser on the CPU. Measured there (desktop settings, median run of three,
the runner's Google Chrome): performance 0.53 to 0.56 and TBT 3.4 to 11.2 s in 60 CI jobs, every job of
the former check "Build + Lighthouse CI" that reported a result for / between 2026-09-30 23:33 and
2026-10-01 08:28 UTC (the public job logs; tools/ops-config.test.ts lists each job id with its
values); LCP within 2.5 s, CLS within 0.1 and accessibility 1 in all of them. In the same period, 2
more stopped on a Lighthouse runtime error (NO_NAVSTART) before asserting. The two jobs before that
period, on 2026-09-30 before the Phase 2 wave B layers were merged, measured a lighter page:
performance 0.59 and 0.61, TBT 2.3 and 1.7 s. Rasterising the globe on the CPU blocks the main thread
for seconds, so total blocking time, 30 % of the performance score, scores 0 there and the
performance score cannot exceed 0.70: the contract targets for performance and TBT are out of reach on
that runner. They describe visitors with a GPU, which it cannot show.
CI therefore holds / to a separate, documented software-GL budget: performance ≥ 0.45 and
TBT ≤ 13500 ms, with accessibility, LCP and CLS at the contract values (lighthouserc.home.json,
check "Build + Lighthouse CI (/, software-GL budget)", run on every push and pull request, never
skipped). Same Chrome (the runner's preinstalled Google Chrome), SwiftShader flags and desktop
settings as the history above, three runs, assertions on the median run:
| Metric | / software-GL budget | Contract (§11) | Basis |
|---|---|---|---|
| Performance | performance ≥ 0.45 | ≥ 0.85 | Lowest CI value 0.53 minus 0.08 (more than twice the observed spread of 0.03). With TBT at 0 the score moves with FCP, speed index, LCP and CLS only: with the FCP, speed index and CLS of a build-sandbox run (0.4 s, 8.6 s, 0.006), LCP at the contract's 2.5 s gives 0.465. |
| Total blocking time | TBT ≤ 13500 ms | ≤ 300 ms | Highest CI value (11.2 s, job 110238095365) plus 20 %, rounded up to the next 500 ms (11207 × 1.2 = 13448). A log-normal fitted to the 60 values puts about 0.3 % of jobs above it. The performance score no longer sees TBT here, so this is the regression check for main-thread work. |
| Accessibility | = 1 | = 1 | Contract value. |
| Largest contentful paint | ≤ 2500 ms | ≤ 2500 ms | Contract value. |
| Cumulative layout shift | ≤ 0.1 | ≤ 0.1 | Contract value. |
godseye-map-globe-drawn | score 1 in every run | The run measured the globe (below). |
This budget catches regressions of the software-rendered globe. It does not show that / meets the
§11 performance and TBT targets, which assume a GPU; only the optional GPU job below measures that.
If the runner's hardware or Chrome changes the numbers for good, re-derive both budget values from the
new CI history with the same rule (lowest value minus 0.08; highest TBT plus 20 %, rounded up to the
next 500 ms) and update lighthouserc.home.json, this table and the job list in
tools/ops-config.test.ts together; the test checks that they agree. A Lighthouse runtime error such
as NO_NAVSTART fails the job without a result; re-run it.
A page that fell back to "WEBGL2 REQUIRED" or "BASEMAP UNAVAILABLE" is much lighter than the globe:
in the build sandbox (2026-10-01, Chromium 141, these flags plus --ignore-certificate-errors for the
sandbox's egress proxy; with --disable-webgl added, or with that flag left out so the basemap style
was unreachable) the fallbacks scored performance 0.82 to 0.88 (0.86 on the median run of
each) with 58 to 181 ms of total blocking time, against 0.52 to 0.57 and 3.4 to 15.8 s for the globe
(six runs), so they would pass this budget, and even the contract's performance and TBT targets,
without earning it.
A globe without its basemap tiles also skips the work of drawing them. So every scored run
carries the in-run audit godseye-map-globe-drawn (tools/lighthouse/home-config.mjs), asserted on
every run, not on the median. In the page load being scored, after the measurement, it requires: no
fallback alert; the MapLibre canvas with a WebGL2 context (the renderer is reported, SwiftShader in
CI, not judged); data-map-ready="true"; a basemap state other than offline; the canvas alone,
screenshotted with every other element hidden, painted (at least 64 colours, no colour over 90 % of
it); the MapLibre worker and at least one OpenFreeMap vector tile (tiles.openfreemap.org) loaded with
HTTP 200; and no failed request for the basemap style or its TileJSON during the load. The last one
catches a run that showed "BASEMAP UNAVAILABLE" while it was measured and recovered before the end
(the map retries a failed style load after 2, 4, 8 and 16 s, then every 30 s). One run that measured anything else fails the
check. The basemap is a third-party service: if OpenFreeMap is unreachable from the runner, the audit
fails ("not measured") and the run should be repeated once the service is back. Locally:
pnpm build && pnpm lhci:home (with Chrome or Chromium installed; CHROME_PATH selects a binary).
/The upgrade: on a hardware GPU, CI also measures / against the full contract thresholds
(performance ≥ 0.85, accessibility = 1, LCP ≤ 2.5 s, CLS ≤ 0.1, TBT ≤ 300 ms, median of three desktop
runs; job "Build + Lighthouse CI (/, GPU runner)", lighthouserc.gpu.json) and checks that every scored
run drew the globe on that GPU: the config adds the in-run audit godseye-map-webgl-hardware, asserted
on every run. Nothing needs it: while the repository variable LIGHTHOUSE_GPU_RUNNER is empty the job
is skipped (shown as skipped, never red, never queued), and / stays gated by the software-GL budget
above. Once the variable names a runner, the job:
ubuntu-24.04 instead and fails in its first step: the check is red ("not measured") within
seconds, never green. When the variable names a label that no online runner carries (mistyped,
runner offline, billing blocked), the job waits as Queued for up to 24 hours and then fails: the
check stays pending, never green;CHROME_PATH at it and builds the app;pnpm lhci:gpu:check (tools/gpu-renderer-check.ts), a fast filter so that a
runner without working hardware WebGL2 never spends minutes on Lighthouse. It launches the same
Chromium binary with the chromeFlags of lighthouserc.gpu.json (a separate Playwright launch:
Playwright's other automation flags remain and chrome-launcher's defaults are absent) and fails unless
WebGL2 runs on a hardware renderer, on a blank canvas and on the map's own canvas at /. SwiftShader,
llvmpipe, lavapipe, any "software" renderer, a context with a major performance caveat, WebGL not
hardware-enabled in Chromium's own report, or no WebGL2 at all fail the job;/, and refuses to start without a passing pre-check on record.
Every run carries the in-run audit godseye-map-webgl-hardware (tools/lighthouse/): in the page load
being scored, and in Lighthouse's own Chromium, it reads the WebGL2 renderer of the MapLibre canvas,
whether a context that refuses a major performance caveat can be created, the browser's full version
and Chromium's GPU feature status;pnpm lhci:gpu:verify): the in-run audit passed with a hardware renderer, the
MapLibre worker and at least one basemap vector tile loaded with HTTP 200, no console error mentions
WebGL, the desktop settings applied, the browser is the pre-checked build, the number of runs is
right, and the blank-canvas check still passes after the runs;lhci assert, with the in-run audit asserted on every run) and
writes the reports. The job summary quotes, for each scored run, the renderer reported by the in-run
audit next to that run's scores. The reports, gpu-renderer.json (pre-check) and gpu-runs.json
(verification) are uploaded as the lighthouse-gpu artifact.Set the variable. Settings → Secrets and variables → Actions → Variables → New repository variable:
name LIGHTHOUSE_GPU_RUNNER, value the runner's label (for example gpu-t4-4core) or a JSON array of
labels (for example ["self-hosted","linux","x64","godseye-gpu"]). It is not a secret, and no keys are
needed. Deleting the variable (or leaving it empty) turns the job back into a skipped one.
Fork pull requests (both options). Under Settings → Actions → General → "Approval for running fork
pull request workflows from contributors", choose "Require approval for all external contributors". A
fork pull request runs its own copy of the workflow, so it can name the GPU runner's label in runs-on
directly and raise timeout-minutes to 360: the routing in ci.yml only keeps unmodified runs off the
GPU runner and is neither a cost guard nor a security boundary. On a GitHub-hosted GPU runner an
approved fork run is billed to the organisation (360 minutes is about $18.72), so read every change to
.github/** and package.json before approving one; on a self-hosted runner the pre-job hook below
refuses fork runs anyway. Never add a pull_request_target trigger. Dependabot pull requests come from
this repository and do receive repository variables (GitHub staff confirmed it in community discussion
44088, March 2023; the Dependabot docs only mention secrets), so every dependency update is measured on
the GPU runner, twice (its push and its pull request).
Option A: GitHub-hosted GPU runner (recommended). GPU runners are larger runners, which GitHub offers
only to organisations on GitHub Team (a paid per-user plan) or Enterprise Cloud, so a repository in a
personal account must first move into such an organisation (Settings → General → Transfer ownership).
Keep a payment method on the organisation (larger runners can take up to 10 minutes to become usable
after one is added) and give the Actions budget a non-zero amount, for example $20 a month with
"Stop usage when budget limit is reached": a $0 budget blocks larger runners, and the job then waits as
Queued. Then, as an organisation owner: organisation Settings → Actions → Runners → New runner → New
GitHub-hosted runner. Name it (the name becomes the label, for example gpu-t4-4core); platform Linux
x64; image: Partner tab, "NVIDIA GPU-Optimized Image for AI and HPC" (GitHub's docs list it as
NVIDIA GPU-Optimized VMI); size: GPU-powered tab (4 vCPU, one Tesla T4, 28 GB RAM, 16 GB VRAM, 176 GB SSD);
maximum concurrency 1; a dedicated runner group. Give that group access to this repository (Repository
access → Selected repositories) and allow public repositories, because runner groups serve private
repositories only by default. Billing is per job, rounded up to whole minutes (linux_4_core_gpu,
$0.052 per minute on 2026-10-01), not covered by included minutes and never free, public repositories
included. One run (build, pre-check, three Lighthouse runs, verification) takes roughly 8 to 15 minutes,
about $0.40 to $0.80, and a push to a branch with an open pull request runs it twice (push and
pull_request). References: larger runners,
runner pricing.
Option B: self-hosted GPU machine. GitHub's guidance is that self-hosted runners "should almost never be used for public repositories", because anyone can open a pull request, and a fork's pull request runs its own copy of the workflow. If you still use one, run it as a just-in-time (JIT) runner, which takes exactly one job and is then removed:
vulkaninfo --summary must list the GPU,
not llvmpipe), jq, Chromium's system libraries (sudo npx [email protected] install-deps chromium;
the workflow never runs sudo on a self-hosted runner), the
runner application, and the pre-job hook below saved
outside the runner directory, for example as /opt/godseye-runner-hooks/job-started.sh (executable,
owned by root) and named in the runner's .env file:
ACTIONS_RUNNER_HOOK_JOB_STARTED=/opt/godseye-runner-hooks/job-started.sh. The runner executes the
hook before every job; a non-zero exit fails the job before any step runs, and a fork cannot edit a
file on your machine;./run.sh --jitconfig "<encoded_jit_config>" in that VM as the runner user, and (4) destroys the VM
when run.sh exits, then starts over. GitHub assigns one job to that runner and removes it afterwards.
While no matching runner is online the / check stays pending ("Waiting for a runner") for up to
24 hours and then fails;self-hosted, linux and x64 next to godseye-gpu, as GitHub's own example
does, because its labels field is documented only as the labels to add;pnpm-lock.yaml, next.config.ts, tools/** and src/**, not only
.github/** and package.json: the job installs, builds, runs and serves that tree.# On the controller host (never inside a runner VM): one just-in-time runner configuration.
curl -sS --fail-with-body -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GODSEYE_RUNNER_ADMIN_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/awne8886/godseye/actions/runners/generate-jitconfig \
-d "{\"name\":\"godseye-gpu-$(date -u +%Y%m%dT%H%M%SZ)\",\"runner_group_id\":1,\"labels\":[\"self-hosted\",\"linux\",\"x64\",\"godseye-gpu\"]}" \
| jq -r .encoded_jit_config
#!/bin/sh
# GODSEYE GPU runner pre-job hook: admit pushes and pull requests from this repository only.
set -eu
case "${GITHUB_EVENT_NAME:-}" in
push) exit 0 ;;
pull_request)
head=$(jq -r '.pull_request.head.repo.full_name // empty' "$GITHUB_EVENT_PATH")
if [ -n "$head" ] && [ "$head" = "${GITHUB_REPOSITORY:-}" ]; then exit 0; fi
;;
esac
echo "GODSEYE GPU runner: refusing a ${GITHUB_EVENT_NAME:-unknown} job from ${GITHUB_REPOSITORY:-unknown}: only pushes and same-repository pull requests run here" >&2
exit 1
Branch protection. In the ruleset for the default branch, require the two Lighthouse checks that
run on every push and pull request, "Build + Lighthouse CI (/docs, /privacy)" and
"Build + Lighthouse CI (/, software-GL budget)", and choose GitHub Actions as their source. Require
"Build + Lighthouse CI (/, GPU runner)" as well only once a GPU runner exists and the variable names it:
while the variable is empty that job is skipped, and GitHub counts a skipped job as passing a required
check, so requiring it then adds nothing. If the former single check "Build + Lighthouse CI" is still
required, replace it, or merges wait for it forever. With the variable set, a pull request from a fork
shows the GPU check red ("not measured") by design, and that failed check stays on the pull request. To
measure it, a maintainer reviews the change, pushes its head unchanged to a branch of this repository
(git fetch origin pull/<number>/head && git push origin FETCH_HEAD:refs/heads/pr-<number>), and merges
through a pull request opened from pr-<number>.
When the renderer check fails on a GPU machine, the job summary, gpu-renderer.json and
gpu-runs.json show what Chromium got, and the "GPU diagnostics" step lists the Vulkan ICD manifests.
Without a Vulkan ICD for the GPU, install the driver's user-space GL/Vulkan package for the same driver
version (on Ubuntu, libnvidia-gl-<driver major>, the major version that nvidia-smi prints). Nothing
can be preinstalled on a GitHub-hosted runner, so there it has to be a workflow step before the
pre-check, guarded by if: runner.environment == 'github-hosted' (the only place this workflow may use
sudo): sudo apt-get install -y libnvidia-gl-<driver major>. Chromium's
GPU-in-headless notes
and server-side GPU notes
point to the Tesla T4 guide these
flags and packages come from. If Vulkan cannot work on that machine, ANGLE's EGL backend is the alternative: replace
--use-angle=vulkan --enable-features=Vulkan --disable-vulkan-surface with
--use-gl=angle --use-angle=gl-egl in lighthouserc.gpu.json; the pre-check and the in-run audit still
gate it. Never add SwiftShader flags to lighthouserc.gpu.json or loosen its thresholds: a red / on a
real GPU is an application performance problem to fix.
Locally, on a machine with a GPU, with the Chromium build CI uses (a branded Google Chrome applies different field-trial settings):
pnpm exec playwright install --no-shell chromium && pnpm build && \
CHROME_PATH="$(node -p "require('@playwright/test').chromium.executablePath()")" pnpm lhci:gpu
These follow from upstream terms, quotas or the visitor's hardware, not from missing work.
BASEMAP INCOMPLETE · N TILES MISSING · RETRYING chip rather than hiding the gap.N OLDER THAN 60 S and turns RECENT/STALE, and areas
outside the tiles are not covered. On a host that feeds adsb.lol, ADSBLOL_REAPI=true (see the upgrades
table) replaces the sweep with one global request every 5 s.unplacedAlerts in /api/weather instead of being drawn at a guessed position.NONCOMMERCIAL=true
(always off when COMMERCIAL_DEPLOYMENT=true); until then the Frontlines layer is hidden and
/api/frontlines answers 403 capability_disabled.GODSEYE is released under the MIT licence. It is an independent project that replicates the features of OSIRIS — OSIRIS © 2026 simplifaisoul, MIT licence — and reuses some of its data tables and behaviours with attribution; the NOTICE section of LICENSE lists files substantially derived from it. GODSEYE does not use the OSIRIS name, logo, links or promotions. World Monitor and Orodruin (AGPL) were studied for ideas only; no code was copied from them.
Map data © OpenStreetMap contributors (ODbL), tiles by OpenFreeMap; satellite imagery: Esri, Vantor, Earthstar Geographics, and the GIS User Community; night lights and imagery courtesy of NASA GIBS. Fonts: Inter, JetBrains Mono and Space Grotesk (SIL Open Font License 1.1), bundled from @fontsource. Every other source and its licence is listed in docs/DATA_SOURCES.md and in the in-app attribution panel.
OPENSANCTIONS_KEYopensanctions |
| OpenSanctions API |
SCANNER_URL, SCANNER_KEY | scanner | Optional allow-listed scanner backend (passive scan types) |
SCANNER_ALLOW_ACTIVE=true | scanner_active | Operator opt-in for active scan types |
COINGECKO_DEMO_KEY | coingecko_demo | CoinGecko demo key |
ANTHROPIC_API_KEY | anthropic | Language-model briefings and overviews |
GEMINI_API_KEY_1 | gemini | Fallback analyst model |
OLLAMA_URL | ollama | Operator-configured local model (visitors can never supply a URL) |
ANTHROPIC_MODEL, GEMINI_MODEL, OLLAMA_MODEL | — | Optional model overrides; by default the newest suitable model from each provider's model list is used |
DISABLE_USER_AI_KEYS=true | ai_user_keys | Refuse visitor-supplied keys (x-ai-key header, used once, never stored) |
pnpm audit without --prod reports two high advisories for
extract-zip 2.0.1 (GHSA-jmr9-qjv8-65gv, GHSA-7pqw-9j4j-h8q3), which has no patched release. It enters
only through @lhci/cli → lighthouse → puppeteer-core → @puppeteer/browsers and is never executed
(Lighthouse CI launches the preinstalled Chromium via CHROME_PATH and downloads nothing). The
production audit (pnpm audit --prod, a CI job) is clean.