Add community node socket: devices, tickets, consent gate, kill switch

Phase 3 of plan.md. Device registration with one-time secrets, scoped single-use
socket tickets, the lock-owner WS /nodes/ws with handshake and heartbeat eviction,
a single batch ConsentGate, and NodesService which disconnects nodes within one tick
when consent, device or the nodes_enabled kill switch changes. A reconcile of 2000
sessions measured about 110 ms after replacing per-session queries with batches.
Devii may withdraw but never grant bandwidth_sharing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 09:14:53 +02:00
co-authored by Claude Sonnet 5.5
parent fad41d0fbe
commit 42b81271b8
41 changed files with 2166 additions and 22 deletions
+33 -1
View File
@@ -12,7 +12,7 @@ from sqlalchemy import text
from devplacepy.config import SECRET_KEY
from devplacepy.services.nodes.spec import DEVICE_KINDS, SPEC
from .core import _now_iso, db, get_table
from .core import _in_clause, _now_iso, db, get_table
from .soft_delete import soft_delete
LIMITS = SPEC.limits
@@ -23,6 +23,7 @@ SECRET_BYTES = 32
LABEL_MAX = 60
EVENT_DETAIL_MAX = 500
MEGABYTE = 1024 * 1024
BATCH_SIZE = 500
HOURS_PATTERN = re.compile(r"^([01]\d|2[0-3]):[0-5]\d$")
PLATFORMS = tuple(sorted({item.adapter for item in SPEC.destinations}))
FLAG_OPEN = "open"
@@ -125,6 +126,37 @@ def get_device(device_uid: str) -> dict | None:
return get_table("node_devices").find_one(uid=device_uid, deleted_at=None)
def _batches(values) -> list[list[str]]:
unique = list(dict.fromkeys(value for value in values if value))
return [unique[i : i + BATCH_SIZE] for i in range(0, len(unique), BATCH_SIZE)]
def get_devices_by_uids(device_uids) -> dict[str, dict]:
found: dict[str, dict] = {}
for batch in _batches(device_uids):
placeholders, params = _in_clause(batch)
for row in db.query(
f"SELECT * FROM node_devices WHERE uid IN ({placeholders}) "
"AND deleted_at IS NULL",
**params,
):
found[row["uid"]] = dict(row)
return found
def get_enabled_by_users(user_uids) -> dict[str, bool]:
found: dict[str, bool] = {}
for batch in _batches(user_uids):
placeholders, params = _in_clause(batch)
for row in db.query(
f"SELECT user_uid, enabled FROM node_settings WHERE user_uid IN ({placeholders}) "
"AND deleted_at IS NULL",
**params,
):
found[row["user_uid"]] = bool(row["enabled"])
return found
def list_devices(user_uid: str) -> list[dict]:
return list(
get_table("node_devices").find(