commands
commands
¶
The signed command envelope, and the eleven checks it must survive.
A command crossing between devices is the mesh's sharpest edge: it makes one machine act on another's word. Spec §9 lists what must be verified, and the value of the list is that it is verified in ONE place — a check spread across call sites is a check that will be forgotten at one of them.
identity · origin · destination · signature · expiry · nonce ·
idempotency · permissions · capabilities · risk · confirmation
Two properties are worth stating plainly, because they are what make the difference between a protocol and a hope:
- The signature covers the whole envelope EXCEPT itself, over canonical bytes. Change one character of one argument and verification fails.
- A nonce may be spent once. Replaying a captured command — the classic way to make "open this" become "open this forty times" — is refused by the second attempt, even with a perfect signature.
Classes¶
CommandError
¶
Bases: RuntimeError
Malformed command — the sender got the protocol wrong.
CommandRejected
¶
Bases: RuntimeError
A well-formed command that must not run.
Carries a machine-readable code for the caller's status field and a
French message safe to show the user (never the failing signature,
never a nonce, never an argument value).
Source code in src/diapason/mesh/commands.py
RemoteCommand
dataclass
¶
RemoteCommand(
command_id: str,
owner_id: str,
origin_device_id: str,
target_device_id: str,
tool: str,
arguments: dict[str, Any],
created_at_ms: int,
expires_at_ms: int,
nonce: str,
idempotency_key: str,
requires_confirmation: bool = False,
confirmation_id: str = "",
signature: str = "",
version: int = COMMAND_VERSION,
_extra: dict[str, Any] = dict(),
)
One instruction from one device to another (spec §8).
Attributes¶
Methods:¶
to_dict
¶
L'enveloppe telle qu'elle part sur le fil.
INVARIANT DU SCELLEMENT, et tout en dépend : .tool,
.arguments et .requires_confirmation portent TOUJOURS le
clair, des deux côtés du réseau. _extra["scelle"] porte le
triplet chiffré exactement tel qu'il a été signé, et c'est LUI que
cette méthode réémet.
Sans cette règle, le récepteur rangerait dans sa file une enveloppe portant le clair sous une signature calculée sur le chiffré — donc une enveloppe qui ne vérifierait plus sa propre signature. C'est le bogue que la première version du plan s'annonçait comme bénéfice avant de l'introduire.
Une commande CLAIRE (_extra vide) produit exactement les mêmes
octets qu'avant le 26 août 2026. C'est ce qui protège le client
mobile figé, et un test le compare à un vecteur gelé plutôt que de
s'en remettre à la lecture.
Source code in src/diapason/mesh/commands.py
NonceStore
¶
Spent nonces, so a captured command cannot be replayed.
Entries are pruned past the maximum command lifetime: a nonce can only be replayed while its command could still be valid, so remembering it beyond that adds storage without adding safety.
Source code in src/diapason/mesh/commands.py
Methods:¶
spend
¶
Consume nonce. False when it was already spent — a replay.
The INSERT itself is the check: relying on the primary key makes the test atomic, where a SELECT-then-INSERT would let two concurrent deliveries of the same command both pass.
Source code in src/diapason/mesh/commands.py
Functions:¶
build_command
¶
build_command(
*,
owner_id: str,
origin_device_id: str,
target_device_id: str,
tool: str,
arguments: Mapping[str, Any] | None = None,
ttl_ms: int = DEFAULT_TTL_MS,
requires_confirmation: bool = False,
confirmation_id: str = "",
idempotency_key: str = "",
) -> RemoteCommand
Assemble an unsigned command with fresh anti-replay material.
Source code in src/diapason/mesh/commands.py
sign_command
¶
sign_command(command: RemoteCommand) -> RemoteCommand
Sign with THIS device's private key, over everything but the signature.
Source code in src/diapason/mesh/commands.py
verify_command
¶
verify_command(
raw: Mapping[str, Any],
*,
registry: Any,
local_device_id: str,
local_owner_id: str,
nonces: NonceStore,
now: int | None = None,
) -> RemoteCommand
Run every check of spec §9. Raises on the first failure.
Order matters: the cheap structural checks come first so a malformed or misaddressed command never reaches the cryptography, and the nonce is spent LAST — otherwise a command rejected for another reason would burn a nonce the legitimate sender still needs.
Source code in src/diapason/mesh/commands.py
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | |