The sky is not something an account grants you
`liveData` was one flag meaning two unrelated things, and it was set to `tier !== "anon"`. That reasoning does not survive asking what the data actually is. Cloud cover over San Francisco is a reading from a government sensor. The aircraft are broadcasting their positions, unencrypted, to anyone within range who owns a forty-dollar receiver. Neither is withheld from anybody by anybody, so neither is a thing an account can grant access to — and putting them behind a sign-in cost the only moment that makes this project land: real fog rolling off the Pacific onto a city you recognise, at the real time of day, on a first visit. On an SSO-gated deployment it cost that moment for every visitor there currently is. So `liveData` splits. `liveEnvironment` is public and unconditional. `liveMarkers` is asked for by everyone and granted by the server, because the marker set is the one feed here that can carry something private — a company's pipeline, a person's job search — and whether it is public is a property of the deployment, not of a file in this repo. `TERA_MARKERS_ACCESS` is therefore a server switch and its default is `members`, which is the safe answer rather than the common one. The failure mode of getting this wrong is silent: nothing throws, nothing looks broken, the data is simply readable by the internet. An operator who wires real markers up gets the shut door without having chosen it and has to say `public` out loud — and saying it appends a line to `degraded[]`, so `/api/v1/health` reports that this box is publishing its map without anyone having to go and read the env file. Same reasoning as `TERA_ADMIN_SUBJECTS=*`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+15
-10
@@ -289,13 +289,18 @@ async function mountCity(id: string) {
|
||||
* The sky and the traffic are per-city and are chosen here, before the build,
|
||||
* because `flights` is fixed at scene construction.
|
||||
*
|
||||
* Two gates, and both are needed. `can.liveData` is the tier — an anonymous
|
||||
* visitor must not be firing requests the server is going to refuse — and
|
||||
* `feeds` is the deployment, which is what stops a member on the ordinary
|
||||
* box, where every source is `none`, from polling two endpoints forever for
|
||||
* a 404. The old code gated the flights on `liveData`, the markers flag,
|
||||
* which is a different feed entirely: a deployment with a real ADS-B receiver
|
||||
* and no marker file flew the simulator.
|
||||
* Still two gates, but only one of them is about the visitor now.
|
||||
*
|
||||
* `can.liveEnvironment` is true for everybody — the reasoning is in
|
||||
* `access.ts`, and it comes down to the sky not being a thing an account can
|
||||
* grant you. What remains load-bearing is `feeds`, the *deployment*: it is
|
||||
* what stops the ordinary box, where every source is `none`, from polling two
|
||||
* endpoints forever for a 404 on every tab that is open.
|
||||
*
|
||||
* The other half of the old comment is still worth keeping, because it was a
|
||||
* real bug: the flights used to be gated on the *markers* flag, which is a
|
||||
* different feed entirely, so a deployment with a real ADS-B receiver and no
|
||||
* marker file flew the simulator.
|
||||
*/
|
||||
const region = regionOf(entry.city);
|
||||
// The hand-authored corridors for *this* city. `SAMPLE_ROUTES` was passed
|
||||
@@ -303,7 +308,7 @@ async function mountCity(id: string) {
|
||||
// entire sky projected ~590 km off the world and rendered as nothing at all.
|
||||
const routes = sampleRoutesFor(entry.city);
|
||||
const traffic =
|
||||
access.can.liveData && access.feeds?.flights ? tera.flights(region, routes) : null;
|
||||
access.can.liveEnvironment && access.feeds?.flights ? tera.flights(region, routes) : null;
|
||||
cityFlights = traffic;
|
||||
|
||||
const handle = await createScene(stage, {
|
||||
@@ -342,7 +347,7 @@ async function mountCity(id: string) {
|
||||
* replaces it when it lands.
|
||||
*/
|
||||
weatherWatch =
|
||||
access.can.liveData && access.feeds?.weather
|
||||
access.can.liveEnvironment && access.feeds?.weather
|
||||
? tera.watchWeather(entry.city.center, () => {
|
||||
updateSun();
|
||||
renderSource();
|
||||
@@ -1434,7 +1439,7 @@ async function boot() {
|
||||
// whether this visitor may ask, `feeds` says whether there is anything to
|
||||
// ask. A box with `markers: "none"` serves the public empty body to everyone,
|
||||
// so the request buys a round trip and lands on the same sample set.
|
||||
if (access.can.liveData && access.feeds?.markers) {
|
||||
if (access.can.liveMarkers && access.feeds?.markers) {
|
||||
try {
|
||||
const feed = await tera.markers();
|
||||
markers = feed.value;
|
||||
|
||||
Reference in New Issue
Block a user