1
0

A plan view in the corner, a night you can actually see, and three kinds of visitor

The right half of the screen was empty sky. It holds the board now, drawn flat,
with the footprint of the camera's own frustum on it — the one part of a minimap
that earns its place, because it answers "where am I looking from" without
leaving the shot. Click it, drag it, scroll it. It is a 2D canvas rather than a
second WebGL context, cached per city and redrawn only when something moved.

Night was black. Not dark — black: at 3 a.m. the coastline, the hills and the
bay were one shape, and the frame read as a failed render rather than as
darkness. The sky already had a floor for exactly this reason and nothing did
the equivalent for the ground, so the ground has one now. The moon still has to
be worth computing, so the gap between a moonlit night and a moonless one is
preserved rather than filled in.

Three tiers, resolved once in the new src/access.ts: anonymous, signed in,
admin. Anonymous gets the map and a public office — the shell, the furniture,
the named viewpoints, nobody home — built without the private objects rather
than with them hidden, because scene.traverse makes hiding a leak with a bow on
it. The time scrubber and the debug readouts are admin only, and admin is
granted by TERA_ADMIN_SUBJECTS on the server and inferred nowhere else. An
unreachable API means member, never god: the promise is "clone it and it works",
not "clone it and you are an administrator of a deployment you did not
configure".

Three things this run found and fixed rather than shipped:

  - entryUrl came off the wire and went straight into an href with no scheme
    check, and a CSP of script-src 'self' 'unsafe-inline' does not stop a
    javascript: URL from navigating. One rejection point in access.ts now.
  - A 5xx from /health was the same null as "no API at all" and therefore the
    opposite conclusion. Eight seconds of tera-api restarting would have told
    every anonymous visitor they were a member. A 5xx is an answer; it fails
    closed.
  - decodeURIComponent in cookieToken was the one path in auth/index.ts that
    threw rather than returning ANONYMOUS, so one malformed cookie header from
    an unauthenticated caller turned /api/v1/session into a 500.

Also: keyboard shortcuts, focus rings, a boot state instead of a blank 2.3
seconds, a collapsible panel under 900px, and no horizontal overflow at 375,
768, 1440 or 2560.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-05 22:53:30 -07:00
parent 47faec9f9d
commit 5bc7258753
24 changed files with 3982 additions and 211 deletions
+3
View File
@@ -35,6 +35,9 @@ describe("a box handed nothing", () => {
assert.equal(config.flights.source, "sim");
assert.equal(config.markers.source, "none");
assert.equal(config.auth.mode, "none");
// Nobody configured an admin, so there is no admin. The god tier has to be
// the thing an empty environment does *not* hand out.
assert.deepEqual(config.auth.admins, { everyone: false, subjects: [] });
assert.equal(config.host, "127.0.0.1");
assert.equal(config.port, 8431);
assert.deepEqual(config.degraded, []);
+20
View File
@@ -91,4 +91,24 @@ describe("other misconfigurations", () => {
const config = loadConfig({ TERA_MARKERS_SOURCE: "file" });
assert.equal(config.markers.source, "none");
});
it("says so when admins are listed on a box where nobody can sign in", () => {
// Nothing falls back here — there is nothing to fall back to — but the
// operator who wrote a name into TERA_ADMIN_SUBJECTS is owed the sentence
// explaining why that name is not getting the time controls.
const config = loadConfig({ TERA_ADMIN_SUBJECTS: "karti" });
assert.equal(config.auth.mode, "none");
assert.deepEqual(config.auth.admins, { everyone: false, subjects: ["karti"] });
assert.match(config.degraded[0] ?? "", /TERA_ADMIN_SUBJECTS/);
assert.match(config.degraded[0] ?? "", /mode=none/);
});
it("records the wildcard as its own line, on top of any other demotion", () => {
const config = loadConfig({ TERA_AUTH_MODE: "jwt", TERA_ADMIN_SUBJECTS: "*" });
// The jwt demotion, the wildcard, and the fact that the wildcard cannot
// reach anybody on a box that just closed itself: three separate facts, and
// health prints all three rather than the first one that happened.
assert.equal(config.degraded.length, 3);
assert.ok(config.degraded.some((line) => line.includes("TERA_ADMIN_SUBJECTS=*")));
});
});
+130
View File
@@ -21,6 +21,7 @@ import { buildApp } from "../app.ts";
import { loadConfig } from "../config.ts";
import { hashPassword, parseScryptHash, verifyPassword } from "../auth/password.ts";
import type { SessionBody } from "../routes/session.ts";
import type { HealthBody } from "../../../src/server/wire.ts";
const SECRET = "not-a-real-secret-and-never-was";
const USER = "karti";
@@ -89,6 +90,7 @@ describe("the login endpoint", () => {
assert.deepEqual(res.json<SessionBody>(), {
authenticated: true,
subject: USER,
admin: false,
passwordLogin: true,
});
@@ -188,6 +190,7 @@ describe("a session cookie and a private office", () => {
assert.deepEqual(anonymous.json<SessionBody>(), {
authenticated: false,
subject: null,
admin: false,
passwordLogin: true,
});
@@ -196,6 +199,7 @@ describe("a session cookie and a private office", () => {
assert.deepEqual(signedIn.json<SessionBody>(), {
authenticated: true,
subject: USER,
admin: false,
passwordLogin: true,
});
});
@@ -212,6 +216,131 @@ describe("a session cookie and a private office", () => {
});
});
/**
* The god tier is a fact the server states and the client repeats. Everything
* below is one shape of the same question — *can the environment, and only the
* environment, decide this?* — so the negatives outnumber the positive again:
* an unlisted account, an anonymous caller under the blanket switch, a name
* that differs only in case. The `*` case is tested for its `degraded` line as
* much as for the grant, because a silent grant-everyone switch is the failure
* this whole setting is shaped around.
*/
describe("the admin tier", () => {
/** Sign in as USER on a box configured this way, and report what it says. */
async function signedIn(env: Record<string, string>): Promise<SessionBody> {
const app = appWith({ ...passwordEnv, ...env });
after(() => app.close());
const cookie = cookiePair((await login(app, USER, PASSWORD)).headers["set-cookie"]);
const state = await app.inject({ method: "GET", url: "/api/v1/session", headers: { cookie } });
return state.json<SessionBody>();
}
it("grants nobody when TERA_ADMIN_SUBJECTS is unset", async () => {
assert.equal((await signedIn({})).admin, false);
// And the safe default is silent: an unset variable is not a demotion.
assert.deepEqual(loadConfig(passwordEnv).degraded, []);
});
it("grants a listed subject, on the login itself and on the session after it", async () => {
const app = appWith({ ...passwordEnv, TERA_ADMIN_SUBJECTS: USER });
after(() => app.close());
const res = await login(app, USER, PASSWORD);
// The POST answers with the real grant for the account that just signed in,
// not a placeholder the following GET would contradict.
assert.deepEqual(res.json<SessionBody>(), {
authenticated: true,
subject: USER,
admin: true,
passwordLogin: true,
});
const cookie = cookiePair(res.headers["set-cookie"]);
const state = await app.inject({ method: "GET", url: "/api/v1/session", headers: { cookie } });
assert.deepEqual(state.json<SessionBody>(), res.json<SessionBody>());
});
it("does not grant an authenticated subject nobody listed", async () => {
const state = await signedIn({ TERA_ADMIN_SUBJECTS: "someone-else,third-party" });
assert.equal(state.authenticated, true);
assert.equal(state.admin, false);
});
it("matches exactly after trimming, and is case-sensitive", async () => {
// Surrounding whitespace is an artefact of writing a list in an env file and
// is dropped; the id itself must be the id.
assert.equal((await signedIn({ TERA_ADMIN_SUBJECTS: " karti , other " })).admin, true);
// Case is not. A subject id is an opaque string an issuer minted, and two
// ids that differ only in case can be two accounts.
assert.equal((await signedIn({ TERA_ADMIN_SUBJECTS: "KARTI" })).admin, false);
});
it("grants everyone under the wildcard, and announces it on health", async () => {
const config = loadConfig({ TERA_OFFICES_DIR: dir, ...passwordEnv, TERA_ADMIN_SUBJECTS: "*" });
config.logLevel = "silent";
const app = buildApp(config);
after(() => app.close());
const cookie = cookiePair((await login(app, USER, PASSWORD)).headers["set-cookie"]);
const state = await app.inject({ method: "GET", url: "/api/v1/session", headers: { cookie } });
assert.equal(state.json<SessionBody>().admin, true);
// The whole point of the wildcard being allowed at all: it cannot be on
// without `/api/v1/health` saying so.
const health = (await app.inject({ method: "GET", url: "/api/v1/health" })).json<HealthBody>();
assert.ok(health.degraded.some((line) => line.includes("TERA_ADMIN_SUBJECTS=*")));
});
it("still refuses the anonymous caller under the wildcard", async () => {
const app = appWith({ ...passwordEnv, TERA_ADMIN_SUBJECTS: "*" });
after(() => app.close());
// "Everyone" means everyone *authenticated*. A browser with no cookie is
// not a member, let alone a god.
const state = await app.inject({ method: "GET", url: "/api/v1/session" });
assert.deepEqual(state.json<SessionBody>(), {
authenticated: false,
subject: null,
admin: false,
passwordLogin: true,
});
});
it("takes the tier away with the session", async () => {
const app = appWith({ ...passwordEnv, TERA_ADMIN_SUBJECTS: USER });
after(() => app.close());
const cookie = cookiePair((await login(app, USER, PASSWORD)).headers["set-cookie"]);
const out = await app.inject({ method: "DELETE", url: "/api/v1/session", headers: { cookie } });
assert.equal(out.statusCode, 200);
assert.deepEqual(out.json<SessionBody>(), {
authenticated: false,
subject: null,
// Said out loud rather than implied by `authenticated: false`, so a page
// holding the old value has something to overwrite it with.
admin: false,
passwordLogin: true,
});
});
it("keeps the list and its size off the health body", async () => {
const app = appWith({
...passwordEnv,
TERA_ADMIN_SUBJECTS: `${USER},someone-else,third-party`,
});
after(() => app.close());
const res = await app.inject({ method: "GET", url: "/api/v1/health" });
const health = res.json<HealthBody>();
// Who the admins are is not a public question, and neither is how many
// there are — a count is an invitation to go looking for the one account.
assert.equal(res.body.includes(USER), false);
assert.equal(res.body.includes("someone-else"), false);
assert.equal(res.body.includes("admin"), false);
assert.deepEqual(health.degraded, []);
});
});
describe("deployments that cannot sign anyone in", () => {
it("leaves mode=none open and offers no login", async () => {
const app = appWith({});
@@ -223,6 +352,7 @@ describe("deployments that cannot sign anyone in", () => {
assert.deepEqual(state.json<SessionBody>(), {
authenticated: false,
subject: null,
admin: false,
passwordLogin: false,
});