Lumbridge Simulate
+Tera · sign in to reach a private office.
+ + + + + +diff --git a/.gitea/workflows/gates.yml b/.gitea/workflows/gates.yml new file mode 100644 index 0000000..b57fe85 --- /dev/null +++ b/.gitea/workflows/gates.yml @@ -0,0 +1,119 @@ +# The gates. +# +# This repo makes two claims about itself — that it is Apache-2.0 clean, and that +# a stranger can self-host it with no account, no key and no network — and until +# this file existed nothing checked either one. Both had been verified per-lens, +# in a unit test or by hand on a box that already had everything, and never once +# end to end on an empty environment. A design critic put it exactly right: +# until one of these jobs exists, self-hostable is a design intention, not a +# property. +# +# Three jobs, one for each claim that can actually be measured: +# +# clean-clone a fresh checkout installs, builds and passes its tests +# zero-config-boot the API answers health when handed nothing at all +# no-binary-art src/** carries no committed binary assets +# +# They are deliberately independent and run in parallel: a broken build should +# not hide a licensing regression. +# +# This runs on Gitea Actions, not GitHub. The syntax is GitHub-compatible and the +# `actions/*` steps resolve through the instance's configured action registry. + +name: gates + +on: + push: + branches: [master] + pull_request: + workflow_dispatch: + +# A superseded push has nothing to tell us, and these jobs build a Docker image +# and bind a port between them. +concurrency: + group: gates-${{ github.ref }} + cancel-in-progress: true + +jobs: + # ---- Job 1: a stranger clones this and it works ---- + # + # The whole job is the dev-kit promise from CONTRACT.md §0. Note what is absent + # and is meant to stay absent: no `env:` block, no `secrets.*` anywhere, and no + # dependency cache. The cache is the interesting omission — a warm cache would + # make this job pass on a lockfile that no longer resolves from a clean state, + # which is precisely the failure a stranger would hit first. + clean-clone: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + # The server runs TypeScript directly by stripping types at load, so + # the floor is real: server/package.json says >=22.18. 24 is what the + # image in server/Dockerfile uses. + node-version: "24" + + - name: npm ci + run: npm ci + + - name: npm run build + # tsc --noEmit, then vite build. A dynamic import of an uninstalled + # package fails here, which is the reason the dependency policy is an + # allowlist rather than a count — see CONTRACT.md §6. + run: npm run build + + - name: server tests + run: npm test --workspace @lumbridge/tera-api + + # ---- Job 2: the box boots on nothing ---- + # + # Two independent server designs defaulted the weather source to a provider + # that needs a contact string and then failed hard without one, which breaks + # the only acceptance test this repo has. This job exists specifically to catch + # that class of bug, and it passes today. + # + # The gate starts the real entry point under a genuinely empty environment + # rather than `docker compose up`, which is the wording in CONTRACT.md §0. The + # property asserted is identical — the compose file adds only TERA_HOST and + # container hardening, and every other variable in it carries a `:-` default so + # an empty environment resolves it to the empty string — while a job that needs + # docker-in-docker present on the runner would be measuring the runner instead + # of the repo. `node scripts/check-zero-config-boot.mjs --compose` runs the + # literal compose form for anyone who has a Docker. + zero-config-boot: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: npm ci + run: npm ci + + - name: health on an empty environment + run: node scripts/check-zero-config-boot.mjs + + # ---- Job 3: no committed binary art ---- + # + # Scoped strictly to tracked files under src/**, because a self-hoster is told + # to put their own legally-clean art in public/props/ and friends and an + # earlier design would have failed their build for doing exactly that. The + # script enumerates with `git ls-files` and has no filesystem-walk fallback. + # See CONTRACT.md §7. + # + # No install step: the check is dependency-free on purpose, so it stays + # runnable by hand and cannot be broken by a bad lockfile. + no-binary-art: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "24" + + - name: no binary art under src/ + run: node scripts/check-no-binaries.mjs diff --git a/.gitignore b/.gitignore index 274275c..9d4e091 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,22 @@ dist/ # runtime and cached in the browser. An Apache-2.0 repo containing them would # be relicensing marks it does not own. See ARCHITECTURE.md §3.1. public/logos/ + +# ---- Self-hoster space ---- +# +# Yours, not ours. Drop your own props, kits, office packs and notes here and +# nothing in this repo will look at them, complain about them or ship them. +# +# This is the other half of the no-binary-art gate. That check +# (scripts/check-no-binaries.mjs) enumerates with `git ls-files` and is strict +# only over src/**, so your own legally-clean .glb sitting in public/props/ is +# invisible to it and cannot fail your build — an earlier design walked the +# working tree and would have. Ignoring these paths is what makes that promise +# hold by default rather than by everybody remembering. See CONTRACT.md §7. +# +# Worth knowing before you write one: docs/ is in here too, so documentation +# committed to this repo lives at the root or beside the code it describes. +public/props/ +public/kits/ +public/offices/ +docs/ diff --git a/index.html b/index.html index cb6b325..848e554 100644 --- a/index.html +++ b/index.html @@ -37,6 +37,16 @@ .enter:hover { background: #ffc555; } .scrub { display: flex; align-items: center; gap: 0.4rem; margin-top: 0.45rem; } .scrub input { flex: 1; accent-color: #f2b134; height: 14px; } + .cities { display: flex; gap: 3px; } + .city { flex: 1; font: inherit; font-size: 11px; padding: 0.35rem; cursor: pointer; + border: 0; border-radius: 5px; background: rgba(8,12,16,0.55); color: rgba(255,255,255,0.6); + backdrop-filter: blur(6px); } + .city:hover { background: rgba(255,255,255,0.14); } + .city.active { background: rgba(242,177,52,0.22); color: #ffd68a; } + .source { position: fixed; left: 1rem; bottom: 1rem; font-size: 10px; + color: rgba(255,255,255,0.4); background: rgba(8,12,16,0.5); padding: 0.25rem 0.5rem; + border-radius: 4px; backdrop-filter: blur(6px); } + .source.live { color: #7ee08a; } .scrub button { font: inherit; font-size: 9px; text-transform: uppercase; letter-spacing: 0.08em; padding: 0.15rem 0.35rem; cursor: pointer; border: 0; border-radius: 3px; background: rgba(255,255,255,0.13); color: rgba(255,255,255,0.7); } @@ -54,11 +64,13 @@ +
+drag to orbit · scroll to zoom
+Tera · sign in to reach a private office.
+ + + + + +diff --git a/public/login.html b/public/login.html new file mode 100644 index 0000000..5a7812f --- /dev/null +++ b/public/login.html @@ -0,0 +1,148 @@ + + +
+ + + +
+ + +
+