{system?.name || 'RouteForge'} {system?.version || 'v0.9.1-rc'}
Current user: {currentUser?.username || '-'}
Role: {currentUser?.role || '-'}
Read-only: {readOnlyLabel}
Mode: {system?.demo_mode ? 'DEMO' : 'LIVE'}
Demo mode is active.
}diff --git a/README.md b/README.md
index fc33d7a..cffc6ca 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
@@ -43,7 +43,7 @@ Routing changes often require fast but traceable checks across multiple external
## Current Alpha Status
-RouteForge is a **functional beta** release with production-like workflows for read-only validation and demo usage. Current release target: **v0.9.1-rc**.
+RouteForge is a **functional beta** release with production-like workflows for read-only validation and demo usage. Current release target: **v0.9.2-rc**.
## Quickstart with Docker Compose
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b6516ac..2565c57 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,8 +1,8 @@
-## v0.9.1-rc (2026-05-21)
+## v0.9.2-rc (2026-05-21)
- UI Cleanup & English-only polish
-## v0.9.1-rc (2026-05-20)
+## v0.9.2-rc (2026-05-20)
### Motivation
Finalize release-candidate validation for deployment, upgrade discipline, security posture, and role-based UX quality before v1.0.
@@ -11,7 +11,7 @@ Finalize release-candidate validation for deployment, upgrade discipline, securi
- Added deployment health check script (`backend/scripts/check_deployment_health.py`) for API/system/database/build smoke validation.
- Expanded operations docs with explicit release QA, upgrade QA, Docker QA, and security checklist guidance.
- Updated release checklist with role/feature UX validation scenarios for admin/operator/viewer.
-- Version bump across backend/frontend/docs to `0.9.1` / `v0.9.1-rc`.
+- Version bump across backend/frontend/docs to `0.9.2` / `v0.9.2-rc`.
### Deployment QA Notes
- Use `python backend/scripts/check_deployment_health.py --base-url http://localhost:8000 --check-setup`.
diff --git a/ROADMAP.md b/ROADMAP.md
index 0f232be..92e1676 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,22 +1,22 @@
# RouteForge Roadmap
## Current Status
-v0.9.1-rc, BGP Visibility Details completed, read-only
+v0.9.2-rc, BGP Visibility Details completed, read-only
-## v0.9.1-rc
+## v0.9.2-rc
- projects/change cases
- grouped preflight reports
-## v0.9.1-rc
+## v0.9.2-rc
- bgp visibility details
-## v0.9.1-rc
+## v0.9.2-rc
- roa planner / roa preflight
-## v0.9.1-rc
+## v0.9.2-rc
- watch mode / scheduled rechecks
-## v0.9.1-rc
+## v0.9.2-rc
- security review
- UX review
- API stability
diff --git a/backend/app/api/routes_checks.py b/backend/app/api/routes_checks.py
index 635b6cf..cfbce6e 100644
--- a/backend/app/api/routes_checks.py
+++ b/backend/app/api/routes_checks.py
@@ -26,7 +26,7 @@ def check_asn(payload: AsnCheckRequest, db: Session = Depends(get_db), user=Depe
except HTTPException:
raise
except Exception as exc:
- raise HTTPException(status_code=500, detail=f"ASN-Prüfung fehlgeschlagen: {exc}") from exc
+ raise HTTPException(status_code=500, detail=f"ASN check failed: {exc}") from exc
@router.post('/asn-rpki', response_model=CheckResponse)
@@ -37,7 +37,7 @@ def check_asn_rpki(payload: AsnRpkiBatchRequest, db: Session = Depends(get_db),
except HTTPException:
raise
except Exception as exc:
- raise HTTPException(status_code=500, detail=f"ASN-RPKI-Batchprüfung fehlgeschlagen: {exc}") from exc
+ raise HTTPException(status_code=500, detail=f"ASN RPKI batch check failed: {exc}") from exc
@router.post('/prefix', response_model=CheckResponse)
@@ -48,7 +48,7 @@ def check_prefix(payload: PrefixCheckRequest, db: Session = Depends(get_db), use
except HTTPException:
raise
except Exception as exc:
- raise HTTPException(status_code=500, detail=f"Prefix-Prüfung fehlgeschlagen: {exc}") from exc
+ raise HTTPException(status_code=500, detail=f"Prefix check failed: {exc}") from exc
@@ -64,7 +64,7 @@ def check_bgp_visibility(payload: BgpVisibilityCheckRequest, db: Session = Depen
except HTTPException:
raise
except Exception as exc:
- raise HTTPException(status_code=500, detail=f"BGP-Visibility-Prüfung fehlgeschlagen: {exc}") from exc
+ raise HTTPException(status_code=500, detail=f"BGP visibility check failed: {exc}") from exc
@router.post('/roa-preflight', response_model=CheckResponse)
def check_roa_preflight(payload: RoaPreflightCheckRequest, db: Session = Depends(get_db), user=Depends(require_operator_or_admin)) -> CheckResponse:
@@ -76,7 +76,7 @@ def check_roa_preflight(payload: RoaPreflightCheckRequest, db: Session = Depends
except HTTPException:
raise
except Exception as exc:
- raise HTTPException(status_code=500, detail=f"ROA-Preflight-Prüfung fehlgeschlagen: {exc}") from exc
+ raise HTTPException(status_code=500, detail=f"ROA preflight check failed: {exc}") from exc
@router.post('/preflight', response_model=CheckResponse)
@@ -87,7 +87,7 @@ def check_preflight(payload: PreflightCheckRequest, db: Session = Depends(get_db
except HTTPException:
raise
except Exception as exc:
- raise HTTPException(status_code=500, detail=f"Preflight-Prüfung fehlgeschlagen: {exc}") from exc
+ raise HTTPException(status_code=500, detail=f"Preflight check failed: {exc}") from exc
def _store_and_respond(db: Session, ctype: str, resource: str, origin_as: str | None, result: dict, user_id: int | None = None, change_case_id: int | None = None) -> CheckResponse:
diff --git a/backend/app/api/routes_system.py b/backend/app/api/routes_system.py
index 05d7d98..9cd9344 100644
--- a/backend/app/api/routes_system.py
+++ b/backend/app/api/routes_system.py
@@ -12,7 +12,7 @@
def system_info():
return {
'name': 'RouteForge',
- 'version': 'v0.9.1-rc',
+ 'version': 'v0.9.2-rc',
'demo_mode': settings.demo_mode,
'read_only': True,
'data_sources': ['RIPEstat', 'RIPEstat Whois/Registry'],
diff --git a/backend/app/core/prefix_evaluation.py b/backend/app/core/prefix_evaluation.py
index 27feb96..8e64ff4 100644
--- a/backend/app/core/prefix_evaluation.py
+++ b/backend/app/core/prefix_evaluation.py
@@ -16,7 +16,7 @@ def evaluate_prefix_overall(
}
if not origin_as:
- return _warning("Keine vollständige Prefix-Origin-Prüfung möglich", "Ohne Origin-AS ist keine vollständige kombinierte Bewertung von RPKI, Registry/IRR und Routing Visibility möglich.")
+ return _warning("No complete prefix-origin check possible", "Without an origin AS, a complete combined assessment of RPKI, registry/IRR, and routing visibility is not possible.")
if CheckStatus.CRITICAL in statuses.values():
if statuses["routing"] == CheckStatus.CRITICAL:
@@ -26,7 +26,7 @@ def evaluate_prefix_overall(
return _critical("Registry/IRR-Origin widerspricht dem angegebenen Origin-AS.", "Ein gefundenes route/route6-Origin weicht vom geprüften Origin-AS ab.")
if statuses["rpki"] == statuses["registry"] == statuses["routing"] == CheckStatus.UNKNOWN:
- return _unknown("Keine belastbare Gesamtbewertung möglich.", "RPKI, Registry/IRR und Routing Visibility liefern keine verlässliche Aussage.")
+ return _unknown("No reliable overall assessment possible.", "RPKI, Registry/IRR und Routing Visibility liefern keine verlässliche Aussage.")
if statuses["rpki"] == CheckStatus.OK and statuses["registry"] == CheckStatus.OK and statuses["routing"] == CheckStatus.OK:
return {
@@ -38,7 +38,7 @@ def evaluate_prefix_overall(
}
if statuses["rpki"] == CheckStatus.OK and statuses["registry"] == CheckStatus.OK and statuses["routing"] == CheckStatus.UNKNOWN:
- return _warning("Routing-Sichtbarkeit konnte nicht belastbar bestimmt werden.", "RPKI und Registry/IRR sind plausibel, aber die Routing-Sichtbarkeit bleibt unklar.")
+ return _warning("Routing visibility could not be determined reliably.", "RPKI und Registry/IRR sind plausibel, aber die Routing-Sichtbarkeit bleibt unklar.")
if CheckStatus.WARNING in statuses.values():
return _warning("Kombinierte Prefix-Bewertung zeigt Warnhinweise.", "Mindestens eine Einzelprüfung meldet unvollständige oder unsichere Daten.")
@@ -55,7 +55,7 @@ def _warning(summary: str, explanation: str) -> dict:
"summary": summary,
"explanation": explanation,
"risk": "Die Gesamtaussage bleibt eingeschränkt.",
- "recommendations": ["Einzelprüfungen und Rohdaten gezielt nacharbeiten."],
+ "recommendations": ["Individual checks und Rohdaten gezielt nacharbeiten."],
}
@@ -64,7 +64,7 @@ def _critical(summary: str, explanation: str) -> dict:
"status": CheckStatus.CRITICAL.value,
"summary": summary,
"explanation": explanation,
- "risk": "Erhöhtes Risiko für Fehlrouting, Erreichbarkeitsprobleme oder Sicherheitsvorfälle.",
+ "risk": "Erhöhtes Risk für Fehlrouting, Erreichbarkeitsprobleme oder Sicherheitsvorfälle.",
"recommendations": ["Abweichung priorisiert prüfen und beheben."],
}
diff --git a/backend/app/core/recommendations.py b/backend/app/core/recommendations.py
index 50817f6..2a39b5f 100644
--- a/backend/app/core/recommendations.py
+++ b/backend/app/core/recommendations.py
@@ -3,12 +3,12 @@
def default_recommendations(status: CheckStatus) -> list[str]:
if status == CheckStatus.OK:
- return ["Keine unmittelbare Aktion erforderlich. Monitoring fortsetzen."]
+ return ["No immediate action required. Continue monitoring."]
if status == CheckStatus.WARNING:
- return ["RPKI/Registry-Daten prüfen und Abdeckung verbessern."]
+ return ["Review RPKI/registry data and improve coverage."]
if status == CheckStatus.CRITICAL:
return ["Origin-AS und ROA sofort verifizieren, da Route verworfen werden kann."]
- return ["Erneut prüfen; externe Datenquelle war unzuverlässig oder nicht erreichbar."]
+ return ["Check again; external data source was unreliable or unavailable."]
def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str | None) -> dict:
@@ -17,11 +17,11 @@ def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str |
return {
"status": CheckStatus.WARNING.value,
"summary": "Origin-AS missing",
- "explanation": "Für eine vollständige RPKI-Prüfung wird ein Origin-AS benötigt.",
- "risk": "Ohne Origin-AS kann nicht geprüft werden, ob eine konkrete Route RPKI-valid wäre.",
+ "explanation": "A complete RPKI check requires an origin AS.",
+ "risk": "Without an origin AS, it cannot be verified whether a route would be RPKI valid.",
"recommendations": [
- "Ergänze das Origin-AS, zum Beispiel AS3333.",
- "Nutze den ASN-Check, um mögliche Origin-AS-Informationen zu finden.",
+ "Provide the origin AS, for example AS3333.",
+ "Use the ASN check to find potential origin AS information.",
],
}
@@ -31,8 +31,8 @@ def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str |
"status": CheckStatus.OK.value,
"summary": "RPKI validation successful",
"explanation": "Das Prefix-Origin-Paar ist durch einen passenden ROA abgedeckt.",
- "risk": "Kein akutes RPKI-Risiko erkennbar.",
- "recommendations": ["Keine akute Maßnahme erforderlich."],
+ "risk": "No immediate RPKI risk detected.",
+ "recommendations": ["No urgent action required."],
}
if normalized == "invalid":
return {
@@ -41,8 +41,8 @@ def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str |
"explanation": "Das Prefix wird mit einem Origin-AS geprüft, das nicht durch einen passenden ROA gedeckt ist.",
"risk": "Validierende Netze können diese Route verwerfen. Dadurch kann Erreichbarkeit verloren gehen.",
"recommendations": [
- "Prüfe, ob das Origin-AS korrekt ist.",
- "Prüfe bestehende ROAs für das Prefix.",
+ "Check whether the origin AS is correct.",
+ "Review existing ROAs for the prefix.",
"Erstelle oder korrigiere den ROA nur, wenn du zur Verwaltung dieser Ressourcen berechtigt bist.",
],
}
@@ -53,8 +53,8 @@ def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str |
"explanation": "Für das Prefix existiert ein ROA, aber nicht für dieses Origin-AS.",
"risk": "Validierende Netze können diese Route verwerfen, weil das Origin-AS nicht autorisiert ist.",
"recommendations": [
- "Origin-AS prüfen.",
- "ROA prüfen.",
+ "Check the origin AS.",
+ "Review the ROA.",
"Nur korrigieren, wenn man zur Verwaltung der Ressource berechtigt ist.",
],
}
@@ -65,9 +65,9 @@ def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str |
"explanation": "Für das Prefix existiert ein ROA, aber die angekündigte Prefix-Länge ist länger als die erlaubte maxLength.",
"risk": "Validierende Netze können diese Route verwerfen, obwohl das AS grundsätzlich passen kann.",
"recommendations": [
- "Angekündigte Prefix-Länge prüfen.",
- "ROA maxLength prüfen.",
- "Keine zu breite maxLength setzen, wenn sie nicht notwendig ist.",
+ "Check announced prefix length.",
+ "Check ROA maxLength.",
+ "Do not use an overly broad maxLength unless necessary.",
],
}
if normalized in {"unknown", "not_found", "unknown_roa", "notfound"}:
@@ -77,7 +77,7 @@ def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str |
"explanation": "Für dieses Prefix-Origin-Paar wurde kein passender ROA gefunden.",
"risk": "Das ist nicht automatisch ein Ausfall, schwächt aber die Routing-Sicherheit.",
"recommendations": [
- "Prüfen, ob ein ROA angelegt werden sollte.",
+ "Check whether a ROA should be created.",
"Nur anlegen, wenn man zur Verwaltung berechtigt ist.",
],
}
@@ -85,11 +85,11 @@ def evaluate_rpki_status(rpki_status: str | None, prefix: str, origin_as: str |
return {
"status": CheckStatus.UNKNOWN.value,
"summary": "RPKI status could not be determined",
- "explanation": "Der RPKI-Status konnte nicht zuverlässig ermittelt werden.",
- "risk": "Die Bewertung ist unvollständig.",
+ "explanation": "The RPKI status could not be determined reliably.",
+ "risk": "The assessment is incomplete.",
"recommendations": [
- "Prüfe die API-Rohdaten.",
- "Wiederhole die Prüfung später.",
+ "Review the API raw data.",
+ "Repeat the check later.",
"Vergleiche bei Bedarf mit einer zweiten Quelle oder einem lokalen RPKI-Validator.",
],
}
diff --git a/backend/app/core/system_status.py b/backend/app/core/system_status.py
index 574a784..69253cb 100644
--- a/backend/app/core/system_status.py
+++ b/backend/app/core/system_status.py
@@ -153,7 +153,7 @@ def build_system_status(engine: Engine | None) -> dict:
return {
"status": "ok",
"name": settings.app_name,
- "version": "v0.9.1-rc",
+ "version": "v0.9.2-rc",
"read_only": True,
"mode": "demo" if settings.demo_mode else "live",
"demo_mode": settings.demo_mode,
diff --git a/backend/app/main.py b/backend/app/main.py
index f778e90..1be6ba2 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -20,7 +20,7 @@
logging.basicConfig(level=getattr(logging, settings.log_level.upper(), logging.INFO))
logger = logging.getLogger("routeforge")
-app = FastAPI(title="RouteForge", version="0.9.1")
+app = FastAPI(title="RouteForge", version="0.9.2")
app.add_middleware(
CORSMiddleware,
diff --git a/backend/app/services/asn_checker.py b/backend/app/services/asn_checker.py
index f07dfc2..f84e6e2 100644
--- a/backend/app/services/asn_checker.py
+++ b/backend/app/services/asn_checker.py
@@ -53,13 +53,13 @@ def check(self, asn_input: str) -> dict:
if "error" in prefixes:
errors.append("announced-prefixes nicht erreichbar")
status = CheckStatus.UNKNOWN.value if errors else CheckStatus.OK.value
- summary = f"ASN {resource} geprüft."
+ summary = f"ASN {resource} checked."
return {
"status": status,
"summary": summary,
- "explanation": "RPKI kann nicht für eine ASN allein bewertet werden. Für RPKI braucht RouteForge konkrete Prefix-Origin-Paare.",
- "risk": "Ohne Prefix-Origin-Paar ist keine direkte RPKI-Gültigkeitsaussage möglich.",
- "recommendations": ["Fehlende Datenquellen erneut abrufen."] if errors else ["Keine unmittelbare Aktion erforderlich."],
+ "explanation": "RPKI cannot be assessed for an ASN alone. RouteForge requires concrete prefix-origin pairs for RPKI.",
+ "risk": "Without a prefix-origin pair, no direct RPKI validity statement is possible.",
+ "recommendations": ["Retry unavailable data sources."] if errors else ["No immediate action required."],
"details": {
"normalized_asn": asn,
"resource": resource,
@@ -144,23 +144,23 @@ def check_rpki_batch(self, asn_input: str, limit: int) -> dict:
else:
status = CheckStatus.OK.value
- summary_text = f"RPKI-Batchprüfung für {resource}: {len(selected)} Prefixe geprüft."
- explanation = "RPKI wurde für sichtbare Prefix-Origin-Paare der ASN geprüft."
- recommendations = ["Kritische Ergebnisse priorisiert prüfen.", "Warnungen auf fehlende ROA-Abdeckung untersuchen."]
+ summary_text = f"RPKI batch check for {resource}: {len(selected)} prefixes checked."
+ explanation = "RPKI was checked for visible prefix-origin pairs of the ASN."
+ recommendations = ["Prioritize review of critical results.", "Investigate warnings about missing ROA coverage."]
if not selected:
- summary_text = f"RPKI-Batchprüfung für {resource} nicht möglich."
- explanation = "Für diese ASN konnten keine auswertbaren Prefixe gefunden werden."
+ summary_text = f"RPKI batch check for {resource} not possible."
+ explanation = "No analyzable prefixes were found for this ASN."
recommendations = [
- "Prüfe, ob die ASN aktuell Prefixe announced.",
- "Wiederhole die Abfrage später.",
- "Prüfe die Rohdaten der announced-prefixes Antwort.",
+ "Check whether the ASN is currently announcing prefixes.",
+ "Retry the query later.",
+ "Review raw data from the announced-prefixes response.",
]
return {
"status": status,
"summary": summary_text,
"explanation": explanation,
- "risk": "Kritische oder warnende Einzelresultate können auf Routing-Risiken hinweisen.",
+ "risk": "Critical or warning-level individual results can indicate routing risks.",
"recommendations": recommendations,
"input": {"asn": resource, "limit": limit},
"checks": None,
@@ -183,7 +183,7 @@ def _build_rpki_batch_metadata(self, prefixes_payload: dict, announced_data: dic
return {
"available": True,
"reason_code": "prefixes_available",
- "message": f"RPKI-Batchprüfung ist möglich. Es wurden {len(extracted_prefixes)} sichtbare Prefixe gefunden.",
+ "message": f"RPKI batch checking is possible. Found {len(extracted_prefixes)} visible prefixes.",
"prefix_count": len(extracted_prefixes),
"can_retry": False,
}
@@ -191,7 +191,7 @@ def _build_rpki_batch_metadata(self, prefixes_payload: dict, announced_data: dic
return {
"available": False,
"reason_code": "announced_prefixes_error",
- "message": "Die angekündigten Prefixe konnten über RIPEstat nicht geladen werden. Eine RPKI-Batchprüfung ist deshalb aktuell nicht möglich.",
+ "message": "Announced prefixes could not be loaded from RIPEstat. RPKI batch checking is currently not possible.",
"prefix_count": 0,
"can_retry": True,
}
@@ -199,14 +199,14 @@ def _build_rpki_batch_metadata(self, prefixes_payload: dict, announced_data: dic
return {
"available": False,
"reason_code": "no_prefixes_extracted",
- "message": "Für diese ASN wurden in der RIPEstat-Antwort keine auswertbaren Prefixe gefunden. Entweder announced die ASN aktuell keine Prefixe in dieser Quelle oder die Datenstruktur konnte nicht interpretiert werden.",
+ "message": "No analyzable prefixes were found in the RIPEstat response for this ASN. The ASN may currently announce no prefixes in this source, or the data structure could not be interpreted.",
"prefix_count": 0,
"can_retry": True,
}
return {
"available": False,
"reason_code": "no_announced_prefixes",
- "message": "Für diese ASN wurden keine sichtbaren Prefixe gefunden. Ohne Prefixe kann RouteForge keine RPKI-Batchprüfung durchführen.",
+ "message": "No visible prefixes were found for this ASN. Without prefixes, RouteForge cannot perform RPKI batch checks.",
"prefix_count": 0,
"can_retry": True,
}
diff --git a/backend/app/services/bgp_visibility_service.py b/backend/app/services/bgp_visibility_service.py
index ba7e1af..8d8cc74 100644
--- a/backend/app/services/bgp_visibility_service.py
+++ b/backend/app/services/bgp_visibility_service.py
@@ -31,30 +31,30 @@ def check(self, prefix: str, expected_origin_as: str | None) -> dict:
if data_unreliable:
status = CheckStatus.UNKNOWN.value
- summary = "Keine belastbaren BGP-Sichtbarkeitsdaten für das Prefix verfügbar."
- recommendations = ["Später erneut prüfen und ein externes Monitoring zur Gegenprüfung verwenden."]
+ summary = "No reliable BGP visibility data is available for the prefix."
+ recommendations = ["Check again later and use external monitoring for cross-validation."]
elif not visible:
status = CheckStatus.CRITICAL.value if normalized_expected else CheckStatus.WARNING.value
- summary = "Das Prefix ist aktuell nicht sichtbar."
- recommendations = ["Ankündigungspfad und Upstream-Policy prüfen.", "Route-Propagation in mehreren Looking-Glasses validieren."]
+ summary = "The prefix is currently not visible."
+ recommendations = ["Check announcement path and upstream policy.", "Validate route propagation across multiple looking glasses."]
elif normalized_expected and not expected_seen:
status = CheckStatus.CRITICAL.value
- summary = f"Das erwartete Origin {normalized_expected} ist für {normalized_prefix} nicht sichtbar."
- recommendations = ["Origin-AS Konfiguration prüfen.", "Mögliche Route-Leaks/Hijacks gegenprüfen."]
+ summary = f"The expected origin {normalized_expected} is not visible for {normalized_prefix} ."
+ recommendations = ["Check origin-AS configuration.", "Investigate potential route leaks/hijacks."]
elif multiple_origins:
status = CheckStatus.WARNING.value
- summary = "Prefix sichtbar, aber mit mehreren Origin-ASNs (MOAS)."
- recommendations = ["Mehrfach-Origin fachlich bestätigen oder unbeabsichtigte Ankündigung beheben."]
+ summary = "Prefix is visible but has multiple origin ASNs (MOAS)."
+ recommendations = ["Confirm multi-origin behavior or fix unintended announcements."]
else:
status = CheckStatus.OK.value
- summary = "Prefix sichtbar und erwartete Origin-AS (falls angegeben) wird gesehen."
- recommendations = ["Weiter beobachten; Ergebnis ist eine Momentaufnahme externer Sichtbarkeitsdaten."]
+ summary = "Prefix is visible and the expected origin AS (if provided) is observed."
+ recommendations = ["Continue monitoring; this result is a point-in-time snapshot of external visibility data."]
return {
"status": status,
"summary": summary,
"explanation": "BGP Visibility basiert auf RIPEstat-Daten und ist read-only.",
- "risk": "Externe Sichtbarkeitsdaten können zeitversetzt oder unvollständig sein.",
+ "risk": "External visibility data may be delayed or incomplete.",
"recommendations": recommendations,
"input": {"prefix": normalized_prefix, "expected_origin_as": normalized_expected},
"checks": None,
diff --git a/backend/app/services/prefix_checker.py b/backend/app/services/prefix_checker.py
index 723341a..71e2ff1 100644
--- a/backend/app/services/prefix_checker.py
+++ b/backend/app/services/prefix_checker.py
@@ -41,11 +41,11 @@ def check(self, prefix: str, origin_as: str | None) -> dict:
warnings: list[str] = []
if whois.get("error") or routing_status.get("error"):
- warnings.append("Mindestens eine zusätzliche Datenquelle war nicht erreichbar.")
+ warnings.append("At least one additional data source was unavailable.")
if rpki_check.get("status") == CheckStatus.UNKNOWN.value and rpki_check.get("raw", {}).get("error"):
- warnings.append("RPKI-Quelle nicht erreichbar oder unvollständig.")
+ warnings.append("RPKI source unavailable or incomplete.")
if routing_visibility_check.get("status") == CheckStatus.UNKNOWN.value:
- warnings.append("Routing-Sichtbarkeit konnte nicht belastbar bestimmt werden.")
+ warnings.append("Routing visibility could not be determined reliably.")
overall = evaluate_prefix_overall(rpki_check, registry_check, routing_visibility_check, normalized_prefix, normalized_origin)
diff --git a/backend/app/services/registry_checker.py b/backend/app/services/registry_checker.py
index 639f4bc..72bafd4 100644
--- a/backend/app/services/registry_checker.py
+++ b/backend/app/services/registry_checker.py
@@ -9,10 +9,10 @@ def check(self, prefix: str, origin_as: str | None, whois_payload: dict) -> dict
"status": CheckStatus.UNKNOWN.value,
"summary": "Registry-/IRR-Daten konnten nicht bestimmt werden",
"explanation": "Die Whois-/Registry-Datenquelle war nicht erreichbar oder lieferte einen Fehler.",
- "risk": "Die Bewertung ist unvollständig.",
+ "risk": "The assessment is incomplete.",
"recommendations": [
"Prüfe die Rohdaten der Registry-Quelle.",
- "Wiederhole die Abfrage später.",
+ "Retry the query later.",
"Vergleiche das Ergebnis mit einer zweiten Registry-/IRR-Quelle.",
],
"raw": whois_payload if isinstance(whois_payload, dict) else {},
@@ -76,7 +76,7 @@ def check(self, prefix: str, origin_as: str | None, whois_payload: dict) -> dict
"status": CheckStatus.CRITICAL.value,
"summary": "Route/route6-Origin widerspricht dem angegebenen Origin-AS",
"explanation": f"Gefundene Origins: {', '.join(sorted(route_origins))}. Erwartet wurde {normalized_origin}.",
- "risk": "Möglicher Konfigurations- oder Registry-Fehler mit Hijack-Risiko.",
+ "risk": "Möglicher Konfigurations- oder Registry-Fehler mit Hijack-Risk.",
"recommendations": [
"Origin-AS und route/route6-Objekte in der zuständigen Registry abgleichen.",
"Fehlerhafte Registry-Einträge korrigieren.",
diff --git a/backend/app/services/roa_planner_service.py b/backend/app/services/roa_planner_service.py
index 0eb25eb..a3b5741 100644
--- a/backend/app/services/roa_planner_service.py
+++ b/backend/app/services/roa_planner_service.py
@@ -67,7 +67,7 @@ def check(self, prefix: str, origin_as: str, max_length: int | None = None) -> d
planned_validation_state = "not_found"
status = "WARNING"
- summary = "Keine passende ROA gefunden; ein read-only Vorschlag wurde erstellt."
+ summary = "No matching ROA found; a read-only proposal was created."
max_length_risk = "none"
if matching_roas:
@@ -86,7 +86,7 @@ def check(self, prefix: str, origin_as: str, max_length: int | None = None) -> d
max_length_risk = "broad"
if status == "OK":
status = "WARNING"
- recommendations.append("Max Length ist relativ breit gewählt; reduzieren Sie die Breite, um Hijack-Risiko zu senken.")
+ recommendations.append("Max Length ist relativ breit gewählt; reduzieren Sie die Breite, um Hijack-Risk zu senken.")
suggested_roa = None if matching_roas else {"prefix": normalized_prefix, "origin_as": normalized_origin, "max_length": effective_max_length}
if suggested_roa:
@@ -95,7 +95,7 @@ def check(self, prefix: str, origin_as: str, max_length: int | None = None) -> d
return {
"status": status,
"summary": summary,
- "recommendations": recommendations or ["Keine zusätzlichen Empfehlungen."],
+ "recommendations": recommendations or ["No additional recommendations."],
"details": {
"prefix": normalized_prefix,
"origin_as": normalized_origin,
diff --git a/backend/app/services/routing_visibility_checker.py b/backend/app/services/routing_visibility_checker.py
index 0c6761a..10bd9d2 100644
--- a/backend/app/services/routing_visibility_checker.py
+++ b/backend/app/services/routing_visibility_checker.py
@@ -58,9 +58,9 @@ def check(self, prefix: str, origin_as: str | None, routing_payload: dict | None
"status": CheckStatus.CRITICAL.value,
"summary": "Visible Origin-AS differs from expected Origin-AS",
"explanation": "Das Prefix ist sichtbar, aber nicht mit dem erwarteten Origin-AS.",
- "risk": "Möglicher Routing-Fehler, falsches Announcement oder Hijack-Risiko.",
+ "risk": "Möglicher Routing-Fehler, falsches Announcement oder Hijack-Risk.",
"recommendations": [
- "Sichtbares Origin-AS prüfen.",
+ "Sichtbares Check the origin AS.",
"BGP Announcement und Upstream-Konfiguration prüfen.",
"RPKI und Registry/IRR-Daten gegenprüfen.",
],
@@ -72,10 +72,10 @@ def _result_unknown(self, payload: dict | None) -> dict:
"status": CheckStatus.UNKNOWN.value,
"summary": "Routing visibility could not be determined",
"explanation": "RIPEstat routing-status did not respond before the configured timeout or returned no usable payload.",
- "risk": "Die Bewertung ist unvollständig.",
+ "risk": "The assessment is incomplete.",
"recommendations": [
"Prüfe die Rohdaten.",
- "Wiederhole die Abfrage später.",
+ "Retry the query later.",
"Vergleiche bei Bedarf mit einer zweiten Routing-Quelle oder einem Looking Glass.",
],
"raw": {"routing_payload": payload or {}},
diff --git a/backend/app/services/rpki_checker.py b/backend/app/services/rpki_checker.py
index 396f0de..55ed95a 100644
--- a/backend/app/services/rpki_checker.py
+++ b/backend/app/services/rpki_checker.py
@@ -22,11 +22,11 @@ def check(self, prefix: str, origin_as: str | None) -> dict:
evaluation = {
"status": CheckStatus.UNKNOWN.value,
"summary": "RPKI status could not be determined",
- "explanation": "Die RIPEstat-Quelle war nicht erreichbar oder lieferte unerwartete Daten.",
- "risk": "Die Bewertung ist unvollständig.",
+ "explanation": "The RIPEstat source was unavailable or returned unexpected data.",
+ "risk": "The assessment is incomplete.",
"recommendations": [
- "Prüfe die API-Rohdaten.",
- "Wiederhole die Prüfung später.",
+ "Review the API raw data.",
+ "Repeat the check later.",
"Vergleiche bei Bedarf mit einer zweiten Quelle oder einem lokalen RPKI-Validator.",
],
}
diff --git a/backend/app/templates/report.html.j2 b/backend/app/templates/report.html.j2
index 6ff5daa..c7d32ad 100644
--- a/backend/app/templates/report.html.j2
+++ b/backend/app/templates/report.html.j2
@@ -18,35 +18,35 @@
Current user: {currentUser?.username || '-'}
Role: {currentUser?.role || '-'}
Read-only: {readOnlyLabel}
Mode: {system?.demo_mode ? 'DEMO' : 'LIVE'}
Demo mode is active.
}alembic current, alembic heads, alembic upgrade head.Current user: {currentUser?.username || '-'}
Role: {currentUser?.role || '-'}
Read-only: {readOnlyLabel}
Mode: {system?.demo_mode ? 'DEMO' : 'LIVE'}
Demo mode is active.
}alembic current, alembic heads, alembic upgrade head.alembic current, alembic heads, alembic upgrade head.RouteForge is a read-only routing operations console for validation workflows.
Read-only: All checks are non-destructive.
Version: v0.9.1-rc
RouteForge is a read-only routing operations console for validation workflows.
Read-only: All checks are non-destructive.
Version: v0.9.2-rc