Merge branch 'main' into feat/live-ops-wall

This commit is contained in:
2026-10-04 18:28:36 -04:00
5 changed files with 262 additions and 7 deletions
+16 -1
View File
@@ -312,7 +312,8 @@ async def delete_node_client(node_id: str) -> None:
async def reconcile_all() -> None:
"""
Rebuild dynsec state for every approved node from Firestore
(node_keys is the source of truth). Called once at c2-core startup,
(node_keys is the source of truth), and delete the login of every
rejected node. Called once at c2-core startup,
after ensure_roles_and_c2core_grant(). Self-heals a lost/corrupted
dynamic-security.json (e.g. volume wiped, or a prior approve/reissue's
dynsec publish silently failed to persist for some other reason) —
@@ -320,6 +321,20 @@ async def reconcile_all() -> None:
dynsec store would lock out every previously-approved node until
someone noticed and manually re-approved each one.
"""
# server-26#195: revoke first. A node rejected before reject_node()
# revoked anything (or whose revoke hit a dynsec failure) still has a
# broker login in dynamic-security.json; startup is where that heals.
# delete_node_client() treats "not found" as success, so this is a no-op
# for every node already revoked.
for node in await fstore.collection_list("nodes", approval_status="rejected"):
node_id = node.get("node_id")
if not node_id:
continue
try:
await delete_node_client(node_id)
except DynsecError as e:
logger.error(f"dynsec reconcile: failed to revoke rejected node {node_id!r}: {e}")
nodes = await fstore.collection_list("nodes", approval_status="approved")
if not nodes:
return