Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 19 additions & 27 deletions .gitguardian.yaml
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
# GitGuardian configuration for this repository.
# Schema reference: https://docs.gitguardian.com/ggshield-docs/reference/secret/ignore
# (config file format defined by ggshield, the engine GitGuardian's scanning uses)
version: 2

secret:
# These two entries silence the "2 secrets uncovered" finding GitGuardian raised against the
# commit history introduced in PR #1 (feat/phase-1-render-kern) and inherited by every PR
# stacked on top of it (#2-#5).
#
# Why they are safe to ignore:
# - Both values are login/password pairs for a MinIO container started by Testcontainers in
# runner/src/test/java/net/onelitefeather/apus/runner/MinioFixtures.java. The container is
# created and destroyed within a single integration test run; nothing outside that test
# process ever talks to it.
# - They were never used against any real MinIO deployment, staging or production system, or
# any service reachable outside the throwaway test container.
# - The root cause is already fixed: MinioFixtures now generates a fresh, random access
# key/secret key on every test run instead of using these fixed literals (see that file's
# ACCESS_KEY/SECRET_KEY fields). These two entries only silence the two now-historical
# occurrences that remain in already-published commits on the stacked PR branches; rewriting
# that history for two harmless test values would be disproportionate.
#
# Scoped to exactly these two literal values -- not a path or file exclusion -- so nothing else
# in this repository is exempted from scanning.
ignored_matches:
- name: MinIO test-container access key (runner integration tests, throwaway container)
match: apustest
- name: MinIO test-container secret key (runner integration tests, throwaway container)
match: apustestsecret
ignored-matches:
# Two MinIO credentials that the phase 1 integration tests used to hard-code.
# They were never real: both were handed to a throwaway Testcontainers MinIO that
# lives for the duration of one test run, and they existed nowhere else.
#
# The cause is fixed — every container test now generates its credentials per run
# from SecureRandom (see runner/src/test/.../MinioFixtures.java and the equivalents
# in the ingest and api modules). These entries exist only because the old literals
# remain in already-published commits, which cannot be rewritten here.
#
# Listed as SHA256 rather than plaintext, so this file does not itself contain the
# strings it exempts.
#
# Do not extend this list to silence new findings. A finding in new code means the
# code is wrong, not the scanner.
- name: "phase 1 MinIO test access key (throwaway container, cause fixed)"
match: bfd5d64da90af877034e91f582242391fea586e6d187a475a3407bf37bd6f422
- name: "phase 1 MinIO test secret key (throwaway container, cause fixed)"
match: 9c721c67b04a5ff4622f5796d44a042c328d5453795ac798e14c7a7a31125bdd
8 changes: 4 additions & 4 deletions docs/superpowers/plans/2026-08-08-phase-1-render-kern.md
Original file line number Diff line number Diff line change
Expand Up @@ -2218,8 +2218,8 @@ import org.testcontainers.utility.DockerImageName;
*/
class RenderEndToEndTest {

private static final String ACCESS_KEY = "apustest";
private static final String SECRET_KEY = "apustestsecret";
private static final String ACCESS_KEY = "<generated-at-test-runtime>";
private static final String SECRET_KEY = "<generated-at-test-runtime>";
private static final String WORLD_BUCKET = "bundles";
private static final String MAP_BUCKET = "maps";

Expand Down Expand Up @@ -2399,8 +2399,8 @@ import org.testcontainers.utility.DockerImageName;
*/
class TelemetryContractTest {

private static final String ACCESS_KEY = "apustest";
private static final String SECRET_KEY = "apustestsecret";
private static final String ACCESS_KEY = "<generated-at-test-runtime>";
private static final String SECRET_KEY = "<generated-at-test-runtime>";

private static Path fixture() {
return Path.of(System.getProperty("user.dir")).getParent().resolve("testdata/mini-world");
Expand Down
54 changes: 33 additions & 21 deletions docs/superpowers/specs/2026-08-08-apus-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -761,27 +761,39 @@ Oberfläche und Identity-Broker dazu kommen erst in Phase 5.
`BlueMapHosting`: Webserver-Deployment, Service, Ingress, Zertifikat, URL im Status.
Ergebnis: Karten sind unter eigener Adresse erreichbar. **Ende des MVP.**

### Phase 4 — Region-Sharding *(nach Spike)*

Vorgeschalteter **Spike**: Zwei Prozesse rendern gleichzeitig benachbarte, disjunkte
Regionsmengen in denselben Map-Storage; anschließend werden alle Zoomstufen auf Löcher und
veraltete Bereiche geprüft. Hintergrund: Lowres-Tiles mitteln über Regionsgrenzen hinweg,
weshalb konkurrierende Shards einander überschreiben könnten. Granulare Speicherung
verhindert Korruption, aber nicht notwendigerweise gegenseitiges Überschreiben aggregierter
Werte.

Fällt der Spike positiv aus: `shards: N` über `Job` mit `completionMode: Indexed`, jeder
Pod verarbeitet seinen Anteil der Regionsliste aus dem Manifest. Umsetzung über einen
eigenen Runner, der `scheduleMapUpdateTask(map, regions)` aufruft — öffentliche API, keine
Reflection. Nebeneffekte: Der Welt-Download parallelisiert mit, und der Fortschritt wird
genauer als BlueMaps eigene Schätzung, weil über bekannte Regionsanzahlen aggregiert wird.

Fällt der Spike negativ aus: Alternative ist ein zweistufiges Verfahren (Shards rendern
Hires-Tiles, ein abschließender Lauf baut die Lowres-Ebenen auf) oder der Verzicht auf
Sharding zugunsten vertikaler Skalierung.

Die Architektur ist bereits sharding-fähig ausgelegt: Regionsliste im Manifest,
`shards`-Feld in der CR, Fortschrittsaggregation im Operator.
### Phase 4 — Region-Sharding *(Spike durchgeführt, Ergebnis: kein Sharding)*

**Der Spike ist gelaufen und negativ ausgefallen.** Bericht:
`docs/superpowers/spikes/2026-08-09-lowres-sharding-spike.md`.

Gemessen wurde ein Referenzlauf (ganze Welt in einem Durchgang) gegen zwei gleichzeitig
laufende Container mit disjunkten, aneinandergrenzenden Regionsmengen im selben
Map-Storage. Ergebnis: **7 von 24 Lowres-Kacheln weichen ab**, dreimal reproduziert; eine
Kachel fällt von 99 % gerendertem Terrain auf 91 % leer. Ein sequenzieller Kontrolllauf
beschädigt sogar 10 von 24 Kacheln — die Reihenfolgeabhängigkeit bestätigt den Mechanismus
unabhängig vom Wettlauf.

Damit ist die frühere Annahme widerlegt, granulare Speicherung schütze ausreichend. Sie
verhindert Korruption einzelner Kacheln, aber nicht, dass zwei Shards dasselbe aggregierte
Lowres-Tile überschreiben.

**Entscheidung: kein Sharding.** Von den beiden in dieser Spec vorgesehenen Alternativen
wird die zweite gewählt — Verzicht zugunsten vertikaler Skalierung über `render-threads`.
Begründung:

- Das zweistufige Verfahren (Shards rendern nur Hires, ein finaler Lauf baut die
Lowres-Ebenen) setzt einen eigenen Runner mit Anbindung an BlueMap-Core voraus. Genau
den schließt §1.4 für den MVP aus, und §2.1 nennt den Grund: BlueMap-Core ist keine
stabile öffentliche API.
- Vertikale Skalierung ist bereits vorhanden und kostet nichts.
- Es gibt bislang keine Welt, deren Renderzeit das Problem rechtfertigt. Ohne diesen
Bedarf wäre Sharding Aufwand gegen ein hypothetisches Problem.

**Was von Phase 4 bleibt:** Die Architektur ist sharding-fähig ausgelegt — die Regionsliste
steht im Bundle-Manifest, `BlueMapMap.spec.shards` existiert. Sollte künftig eine Welt
tatsächlich zu lange brauchen, ist der zweistufige Weg der dann zu prüfende Ansatz, und
der Spike-Bericht ist die Grundlage dafür. `shards` bleibt bis dahin auf `1` beschränkt;
ein höherer Wert wird nicht umgesetzt und sollte vom Operator abgelehnt werden.

### Phase 5 — API, UI und Mandanten

Expand Down
Loading