Skip to content

Commit 7f0ffec

Browse files
rwilliamspbg-opsCopilotgithub-advanced-security[bot]
authored
Harden frontend backend proxy and build workflow health checks (#101)
* chore: verify CI lint workflow and apply black/lint updates * Overhaul autonomy docs and harden CPU chaos validation - Add comprehensive autonomy core package (contracts, map/twin state, planner, predictor, readiness) with tests and integration points. - Extend backend with autonomy insight endpoints and align OpenAPI coverage. - Expand HUD and C2 surfaces with autonomy KPI, safety, recommendation, and timeline overlays. - Normalize drone telemetry ingest confidence/health metadata and map query contract support. - Switch node-agent to CPU-only PyTorch wheels to reduce container footprint in CI/dev runs. - Harden chaos suite fallback by passing admin auth headers for trigger_fl. - Fix monitoring package integration script/test wiring and refresh repository docs with a full autonomy operations guide plus updated README/C2/monitoring/changelog. * Add concrete AI interaction implementation plan - Turn the AI usability recommendations into a phased, ticketable plan. - Cover command bar, structured recommendations, approval flow, model routing, metadata, mission context, and search. - Define rollout criteria, validation strategy, and first-branch execution order for the new feature branch. * Add shared AI interaction summary and operator UX - add the /ai/interaction/summary backend payload with quick actions and recommendations - wire the HUD and C2 surfaces to the shared interaction summary - fix the app-shell fetch path, add focused C2 coverage, and refresh docs - update the AI interaction plan to record completed work and follow-up items * Complete AI interaction review workflow - add interaction history and decision logging endpoints on the backend - wire the HUD review drawer with approve, edit, reject, and undo flows - pass review metadata through the app shell and action callbacks - update C2, docs, and API specs to reflect the completed interaction plan * Enhance HUD digital twin lens Add digital twin lens panels for map freshness, route candidates, replay context, and envelope assumptions to strengthen operator twin perspective. * Add phase 4 AI interaction concrete execution plan * Potential fix for pull request finding 'CodeQL / Unreachable code' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Ryan <221235059+rwilliamspbg-ops@users.noreply.github.com> * Execute phase 4 interaction auth, replay UX, and contracts * Fix PR check failures: dedupe interaction routes and format Python * Harden frontend backend proxy and main workflow health checks * Fix duplicate AI interaction routes and black formatting in backend Agent-Logs-Url: https://github.com/rwilliamspbg-ops/Sovereign_Map_Federated_Learning/sessions/5439c336-47b4-4fb8-81d8-f9f5373ac766 Co-authored-by: rwilliamspbg-ops <221235059+rwilliamspbg-ops@users.noreply.github.com> --------- Signed-off-by: Ryan <221235059+rwilliamspbg-ops@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent 3ae4dd0 commit 7f0ffec

5 files changed

Lines changed: 45 additions & 57 deletions

File tree

.github/workflows/build.yml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,32 @@ jobs:
7272
- name: Start services
7373
run: docker compose -f docker-compose.full.yml up -d --scale node-agent=5
7474

75-
- name: Wait for backend health
75+
- name: Wait for backend and proxy health
7676
run: |
77-
echo "Waiting for federated learning nodes to initialize..."
78-
sleep 45
77+
set -euo pipefail
78+
echo "Waiting for backend container health and frontend proxy readiness..."
79+
80+
# Wait for backend container to report healthy.
81+
for i in $(seq 1 30); do
82+
status=$(docker inspect -f '{{.State.Health.Status}}' sovereign-backend 2>/dev/null || true)
83+
if [ "$status" = "healthy" ]; then
84+
echo "backend health check passed"
85+
break
86+
fi
87+
if [ "$i" -eq 30 ]; then
88+
echo "backend did not become healthy"
89+
docker compose -f docker-compose.full.yml logs backend || true
90+
exit 1
91+
fi
92+
echo "backend health: ${status:-unknown} (attempt $i/30)"
93+
sleep 5
94+
done
95+
96+
# Validate direct backend endpoint and frontend reverse-proxy endpoint.
97+
curl -fsS --retry 10 --retry-all-errors --retry-delay 3 \
98+
http://127.0.0.1:8000/ops/health >/dev/null
99+
curl -fsS --retry 10 --retry-all-errors --retry-delay 3 \
100+
http://127.0.0.1:3000/backend/ops/health >/dev/null
79101
80102
- name: Debug on failure
81103
if: failure()

docker-compose.full.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ services:
1313
environment:
1414
- NODE_ENV=production
1515
depends_on:
16-
- backend
16+
backend:
17+
condition: service_healthy
1718
networks:
1819
- sovereign-network
1920
restart: always
@@ -31,7 +32,8 @@ services:
3132
environment:
3233
- NODE_ENV=production
3334
depends_on:
34-
- backend
35+
backend:
36+
condition: service_healthy
3537
networks:
3638
- sovereign-network
3739
restart: always

frontend/nginx.conf

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ server {
44
root /usr/share/nginx/html;
55
index index.html;
66

7+
# Resolve Docker service names at request time so proxying survives
8+
# backend container restarts/IP churn on the compose network.
9+
resolver 127.0.0.11 valid=10s ipv6=off;
10+
resolver_timeout 5s;
11+
712
location / {
813
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
914
add_header Pragma "no-cache" always;
@@ -22,29 +27,35 @@ server {
2227
}
2328

2429
location /backend/ {
25-
proxy_pass http://backend:8000/;
30+
set $backend_upstream http://backend:8000;
31+
rewrite ^/backend/(.*)$ /$1 break;
32+
proxy_pass $backend_upstream;
2633
proxy_http_version 1.1;
2734
proxy_set_header Upgrade $http_upgrade;
28-
proxy_set_header Connection 'upgrade';
35+
proxy_set_header Connection "";
2936
proxy_set_header Host $host;
3037
proxy_cache_bypass $http_upgrade;
3138
proxy_set_header X-Real-IP $remote_addr;
3239
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
40+
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
3341
}
3442

3543
location = /api {
3644
return 301 /api/;
3745
}
3846

3947
location /api/ {
40-
proxy_pass http://backend:8000/;
48+
set $backend_upstream http://backend:8000;
49+
rewrite ^/api/(.*)$ /$1 break;
50+
proxy_pass $backend_upstream;
4151
proxy_http_version 1.1;
4252
proxy_set_header Upgrade $http_upgrade;
43-
proxy_set_header Connection 'upgrade';
53+
proxy_set_header Connection "";
4454
proxy_set_header Host $host;
4555
proxy_cache_bypass $http_upgrade;
4656
proxy_set_header X-Real-IP $remote_addr;
4757
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
58+
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504;
4859
}
4960

5061
error_page 500 502 503 504 /50x.html;

frontend/src/HUD.test.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,5 +270,4 @@ describe('HUD AI interaction console', () => {
270270
expect(within(replayArticle).getByText(/reason maintenance window fit/i)).toBeInTheDocument();
271271
expect(within(replayArticle).getByText(/prompt replan corridor alpha/i)).toBeInTheDocument();
272272
});
273-
274273
});

sovereignmap_production_backend_v2.py

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ def _enforce_rate_limit(req: Request) -> Optional[Response]:
450450
bucket.append(now)
451451
return None
452452

453+
453454
@app.before_request
454455
def _security_guardrails():
455456
if request.method == "OPTIONS":
@@ -3955,53 +3956,6 @@ def runtime_profile_view():
39553956
return jsonify({"status": "ok", "active_profile": applied}), 200
39563957

39573958

3958-
@app.route("/ai/interaction/history", methods=["GET"])
3959-
def ai_interaction_history_view():
3960-
try:
3961-
limit = int(request.args.get("limit", 50))
3962-
except (TypeError, ValueError):
3963-
limit = 50
3964-
limit = max(1, min(limit, 200))
3965-
history = _interaction_review_history(limit=limit)
3966-
return jsonify({"count": len(history), "decisions": history}), 200
3967-
3968-
3969-
@app.route("/ai/interaction/decision", methods=["POST"])
3970-
def ai_interaction_decision_view():
3971-
body = request.get_json(silent=True) or {}
3972-
if not isinstance(body, dict):
3973-
return jsonify({"error": "invalid_payload", "message": "JSON object required"}), 400
3974-
3975-
decision = str(body.get("decision", "")).strip().lower()
3976-
if decision not in {"approve", "edit", "reject", "undo"}:
3977-
return jsonify({"error": "invalid_decision"}), 400
3978-
3979-
review_entry = _append_interaction_review_entry(
3980-
{
3981-
"review_id": body.get("review_id"),
3982-
"ts": time.time(),
3983-
"action_id": body.get("action_id"),
3984-
"action_label": body.get("action_label"),
3985-
"action_kind": body.get("action_kind"),
3986-
"model_route": body.get("model_route"),
3987-
"decision": decision,
3988-
"reason": body.get("reason"),
3989-
"prompt": body.get("prompt"),
3990-
"command": body.get("command"),
3991-
"parameters": body.get("parameters") or {},
3992-
"reversible": bool(body.get("reversible", False)),
3993-
"source_action_id": body.get("source_action_id"),
3994-
"override_prompt": body.get("override_prompt"),
3995-
"rollback_of": body.get("rollback_of"),
3996-
"reviewed_by": _sanitize_swarm_token(
3997-
request.headers.get("X-API-Role", "operator"), max_len=24
3998-
).lower()
3999-
or "operator",
4000-
}
4001-
)
4002-
return jsonify({"status": "recorded", "decision": review_entry}), 200
4003-
4004-
40053959
@app.route("/verification_policy", methods=["POST"])
40063960
def update_verification_policy():
40073961
data = request.get_json(silent=True) or {}

0 commit comments

Comments
 (0)