SoCal, the whole bay, a moon, and gates that actually run
Six agents in parallel, and the two city packs independently reported the same blocker: `focusRegions` and `coarseFactor` existed on the `City` type and nothing implemented them. Uniform lattices would have been 2.9M points for Southern California and 3.7M for the expanded bay. Both packs were unloadable as written. `buildAxis` is the answer, and it is honest about its limits: refinement is per axis, not per rectangle, so a focus region sharpens its whole row *and* its whole column. Two regions at opposite corners refine nearly everything between them. Measured, not guessed — the bay went 0.53M points with one region and 1.64M with three, for detail nobody is looking at from a board this wide. One region each, coarse factor ten, and the builds land at 3.8 s and 2.3 s. Then three things that were only ever right because San Francisco was the only city. `maxDistance: 340` and a 170-unit shadow box were constants tuned for a 230-unit board; the bay is 1003 units across and the camera physically could not retreat far enough to frame it. Fog distances were scene units pinned to the same assumption. And `minVisibilityM` defaulted to 4.5 km of honest weather, which over ninety-four kilometres of bay correctly hides three quarters of it — the night view was a black rectangle for a completely reasonable reason. All three now derive from the board. The moon is a real ephemeris and its light is a deliberate lie: 1.15, against a physical ratio of one to four hundred thousand. What is being reproduced is what a moonlit night looks like on a screen in a lit room. The CI gate caught itself, which is the part worth keeping. Port 8431 was already held by a server from an earlier session, so the boot check polled a healthy stranger while the process it started died on EADDRINUSE. It now refuses to run rather than pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+114
-3
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { parseScryptHash, type ScryptHash } from "./auth/password.ts";
|
||||
import type {
|
||||
AuthMode,
|
||||
FlightsSourceId,
|
||||
@@ -58,6 +59,23 @@ export interface MarkersConfig {
|
||||
ttlSeconds: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The one account `TERA_AUTH_MODE=password` will sign in, once the environment
|
||||
* has been read and found to contain a usable credential. `null` everywhere
|
||||
* else, and `routes/session.ts` reads that null as "this box cannot sign anyone
|
||||
* in" and answers 404 on the login endpoint.
|
||||
*/
|
||||
export interface PasswordLogin {
|
||||
username: string;
|
||||
/** Parsed at boot so a typo in the hash is a health line, not a login that never works. */
|
||||
hash: ScryptHash;
|
||||
/** Lifetime of the token this box issues for itself, in seconds. */
|
||||
sessionTtlSeconds: number;
|
||||
/** Failed attempts one IP may make inside `rateWindowSeconds` before 429s start. */
|
||||
rateAttempts: number;
|
||||
rateWindowSeconds: number;
|
||||
}
|
||||
|
||||
export interface AuthConfig {
|
||||
mode: AuthMode;
|
||||
/** Where a browser sends someone to sign in. `sso` mode only. */
|
||||
@@ -73,6 +91,8 @@ export interface AuthConfig {
|
||||
jwksUrl: string;
|
||||
issuer: string;
|
||||
audience: string;
|
||||
/** Set only by `TERA_AUTH_MODE=password`; see `PasswordLogin` and `loadPasswordLogin`. */
|
||||
passwordLogin: PasswordLogin | null;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
@@ -243,11 +263,17 @@ function loadMarkers(env: Env, degraded: string[]): MarkersConfig {
|
||||
};
|
||||
}
|
||||
|
||||
const AUTH_MODES: AuthMode[] = ["none", "sso", "jwt"];
|
||||
/**
|
||||
* `password` is a mode an operator asks for, not a mode the rest of the server
|
||||
* ever sees. It resolves to `jwt` below; see `loadPasswordLogin` for why.
|
||||
*/
|
||||
type ConfiguredAuthMode = AuthMode | "password";
|
||||
|
||||
const AUTH_MODES: ConfiguredAuthMode[] = ["none", "sso", "jwt", "password"];
|
||||
|
||||
function loadAuth(env: Env, degraded: string[]): AuthConfig {
|
||||
const asked = str(env, "TERA_AUTH_MODE", "none");
|
||||
let mode = oneOf(asked, AUTH_MODES);
|
||||
let mode: ConfiguredAuthMode | null = oneOf(asked, AUTH_MODES);
|
||||
if (mode === null) {
|
||||
degraded.push(
|
||||
`TERA_AUTH_MODE="${asked}" is not one of ${AUTH_MODES.join(", ")}; ` +
|
||||
@@ -260,7 +286,8 @@ function loadAuth(env: Env, degraded: string[]): AuthConfig {
|
||||
const revalidateUrl = str(env, "TERA_AUTH_REVALIDATE_URL", "");
|
||||
const jwtSecret = str(env, "TERA_AUTH_JWT_SECRET", "");
|
||||
const jwksUrl = str(env, "TERA_AUTH_JWKS_URL", "");
|
||||
const jwtVerify = str(env, "TERA_AUTH_JWT_VERIFY", "hs256") === "jwks" ? "jwks" : "hs256";
|
||||
let jwtVerify: "hs256" | "jwks" =
|
||||
str(env, "TERA_AUTH_JWT_VERIFY", "hs256") === "jwks" ? "jwks" : "hs256";
|
||||
|
||||
// A demotion here has teeth: it takes private offices with it, which is the
|
||||
// safe direction. Unverifiable credentials must never mean "let them in".
|
||||
@@ -287,6 +314,33 @@ function loadAuth(env: Env, degraded: string[]): AuthConfig {
|
||||
mode = "none";
|
||||
}
|
||||
|
||||
// `password` collapses into `jwt` here, and that is the whole design rather
|
||||
// than an implementation detail. `routes/session.ts` signs exactly the token
|
||||
// `jwt` mode already verifies, so a browser that signed in on this box and a
|
||||
// browser carrying a token from the fleet's issuer arrive at `offices.ts`
|
||||
// through the same `resolve()` — one authorisation path that can be reasoned
|
||||
// about, rather than two that have to be kept in agreement. What `password`
|
||||
// adds is an issuer, not a verifier.
|
||||
let passwordLogin: PasswordLogin | null = null;
|
||||
if (mode === "password") {
|
||||
passwordLogin = loadPasswordLogin(env, jwtSecret, degraded);
|
||||
if (passwordLogin === null) {
|
||||
mode = "none";
|
||||
} else {
|
||||
mode = "jwt";
|
||||
if (jwtVerify === "jwks") {
|
||||
// Verifying against someone else's public keys while signing with a
|
||||
// local secret means this box would reject its own sessions.
|
||||
degraded.push(
|
||||
"TERA_AUTH_MODE=password issues its own HS256 tokens, so " +
|
||||
"TERA_AUTH_JWT_VERIFY=jwks was ignored; this box verifies the " +
|
||||
"sessions it signs.",
|
||||
);
|
||||
jwtVerify = "hs256";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
mode,
|
||||
entryUrl,
|
||||
@@ -297,6 +351,63 @@ function loadAuth(env: Env, degraded: string[]): AuthConfig {
|
||||
jwksUrl,
|
||||
issuer: str(env, "TERA_AUTH_JWT_ISSUER", ""),
|
||||
audience: str(env, "TERA_AUTH_JWT_AUDIENCE", ""),
|
||||
passwordLogin,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The credential for `TERA_AUTH_MODE=password`, or `null` with a loud line if
|
||||
* the environment did not supply a usable one.
|
||||
*
|
||||
* Every failure here is a demotion to `mode=none` rather than a refusal to boot,
|
||||
* which is the rule the whole file follows — but note which direction the
|
||||
* demotion runs: mode=none makes every private office answer 404 to everybody,
|
||||
* including the operator. Nobody is let in by a misconfiguration.
|
||||
*
|
||||
* `TERA_AUTH_PASSWORD_HASH` is a hash and there is deliberately no plaintext
|
||||
* equivalent to set. `auth/password.ts` carries the command that produces one.
|
||||
*/
|
||||
function loadPasswordLogin(
|
||||
env: Env,
|
||||
jwtSecret: string,
|
||||
degraded: string[],
|
||||
): PasswordLogin | null {
|
||||
const username = str(env, "TERA_AUTH_PASSWORD_USER", "");
|
||||
const encoded = str(env, "TERA_AUTH_PASSWORD_HASH", "");
|
||||
|
||||
if (username === "" || encoded === "") {
|
||||
degraded.push(
|
||||
"TERA_AUTH_MODE=password needs TERA_AUTH_PASSWORD_USER and " +
|
||||
"TERA_AUTH_PASSWORD_HASH (a scrypt hash — see server/src/auth/password.ts " +
|
||||
"for the one-liner that prints one). Demoted to mode=none; private " +
|
||||
"offices will answer 404 to everyone.",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (jwtSecret === "") {
|
||||
degraded.push(
|
||||
"TERA_AUTH_MODE=password needs TERA_AUTH_JWT_SECRET to sign the session " +
|
||||
"cookie it issues — try `openssl rand -base64 48`. Demoted to mode=none.",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const hash = parseScryptHash(encoded);
|
||||
if (hash === null) {
|
||||
degraded.push(
|
||||
"TERA_AUTH_PASSWORD_HASH is not a hash this server can read; it should " +
|
||||
"look like `scrypt$16384$8$1$<salt>$<key>`. Demoted to mode=none.",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
username,
|
||||
hash,
|
||||
sessionTtlSeconds: num(env, "TERA_AUTH_SESSION_TTL", 43_200, degraded),
|
||||
rateAttempts: num(env, "TERA_AUTH_LOGIN_ATTEMPTS", 8, degraded),
|
||||
rateWindowSeconds: num(env, "TERA_AUTH_LOGIN_WINDOW", 300, degraded),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user