Let edge nodes read /systems with their own api_key
Build & Deploy / Build & push images (push) Successful in 4m26s
Build & Deploy / Deploy to VM (push) Successful in 1m55s

The node builds its OP25 config from GET /systems, but that router only
accepted a Firebase token or the shared service key — a node holds neither.
Every fetch returned 401 and the node fell back to its stale offline cache,
so a system edited in the UI never reached the field. Confirmed on node-002
against the live server: "Failed to fetch systems from C2: 401 Unauthorized
... Offline cache will be used."

The node sends no node_id with the request, only the bearer token, so the
key is matched by querying node_keys for the value instead of fetching a
known document the way /upload does.

Read access only: the mutating routes in this router each carry their own
require_admin_token, so widening the router-level gate doesn't let a node
create, edit or delete a system.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-08-16 14:19:04 -04:00
co-authored by Claude Opus 5
parent 55cd1110df
commit a195563da6
2 changed files with 45 additions and 2 deletions
+10 -2
View File
@@ -9,7 +9,11 @@ from app.internal.summarizer import summarizer_loop
from app.internal.vocabulary_learner import vocabulary_induction_loop
from app.internal.recorrelation_sweep import recorrelation_loop
from app.config import settings
from app.internal.auth import require_firebase_token, require_service_or_firebase_token
from app.internal.auth import (
require_firebase_token,
require_service_or_firebase_token,
require_node_service_or_firebase_token,
)
from app.routers import nodes, systems, calls, upload, tokens, incidents, alerts, admin, trips, places, links, users
from app.routers import enrollment
from app.internal import dynsec
@@ -81,7 +85,11 @@ app.add_middleware(
)
app.include_router(nodes.router, dependencies=[Depends(require_service_or_firebase_token)])
app.include_router(systems.router, dependencies=[Depends(require_service_or_firebase_token)])
# systems is the one router edge nodes read directly (system_cacher.py builds
# the OP25 config from it), so its gate also accepts a per-node api_key. The
# write routes inside carry their own require_admin_token, so nodes get read
# access only.
app.include_router(systems.router, dependencies=[Depends(require_node_service_or_firebase_token)])
app.include_router(calls.router, dependencies=[Depends(require_service_or_firebase_token)])
app.include_router(tokens.router, dependencies=[Depends(require_service_or_firebase_token)])
app.include_router(incidents.router, dependencies=[Depends(require_service_or_firebase_token)])