instar 1.3.615 → 1.3.616
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/mandates.js +30 -9
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +53 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +23 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/coordination/AccountFollowMeMandateDelivery.d.ts +64 -0
- package/dist/coordination/AccountFollowMeMandateDelivery.d.ts.map +1 -0
- package/dist/coordination/AccountFollowMeMandateDelivery.js +92 -0
- package/dist/coordination/AccountFollowMeMandateDelivery.js.map +1 -0
- package/dist/coordination/DeliveredMandateStore.d.ts +67 -0
- package/dist/coordination/DeliveredMandateStore.d.ts.map +1 -0
- package/dist/coordination/DeliveredMandateStore.js +80 -0
- package/dist/coordination/DeliveredMandateStore.js.map +1 -0
- package/dist/core/AccountFollowMeSpendSlice.d.ts +197 -0
- package/dist/core/AccountFollowMeSpendSlice.d.ts.map +1 -0
- package/dist/core/AccountFollowMeSpendSlice.js +196 -0
- package/dist/core/AccountFollowMeSpendSlice.js.map +1 -0
- package/dist/core/AnthropicSubscriptionRouter.d.ts +28 -0
- package/dist/core/AnthropicSubscriptionRouter.d.ts.map +1 -1
- package/dist/core/AnthropicSubscriptionRouter.js +17 -0
- package/dist/core/AnthropicSubscriptionRouter.js.map +1 -1
- package/dist/core/MeshRpc.d.ts +37 -1
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +30 -0
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/server/AgentServer.d.ts +27 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +80 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +31 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +93 -3
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +47 -47
- package/upgrades/1.3.616.md +34 -0
- package/upgrades/side-effects/ws52-account-follow-me-r7a-spend-slice.md +70 -0
- package/upgrades/side-effects/ws52-one-dashboard-mandate-delivery.md +74 -0
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-17T17:02:07.951Z",
|
|
5
|
+
"instarVersion": "1.3.616",
|
|
6
6
|
"entryCount": 201,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -418,7 +418,7 @@
|
|
|
418
418
|
"type": "route-group",
|
|
419
419
|
"domain": "monitoring",
|
|
420
420
|
"sourcePath": "src/server/routes.ts",
|
|
421
|
-
"contentHash": "
|
|
421
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
422
422
|
"since": "2025-01-01"
|
|
423
423
|
},
|
|
424
424
|
"route-group:agents": {
|
|
@@ -426,7 +426,7 @@
|
|
|
426
426
|
"type": "route-group",
|
|
427
427
|
"domain": "sessions",
|
|
428
428
|
"sourcePath": "src/server/routes.ts",
|
|
429
|
-
"contentHash": "
|
|
429
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
430
430
|
"since": "2025-01-01"
|
|
431
431
|
},
|
|
432
432
|
"route-group:backups": {
|
|
@@ -434,7 +434,7 @@
|
|
|
434
434
|
"type": "route-group",
|
|
435
435
|
"domain": "operations",
|
|
436
436
|
"sourcePath": "src/server/routes.ts",
|
|
437
|
-
"contentHash": "
|
|
437
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
438
438
|
"since": "2025-01-01"
|
|
439
439
|
},
|
|
440
440
|
"route-group:git": {
|
|
@@ -442,7 +442,7 @@
|
|
|
442
442
|
"type": "route-group",
|
|
443
443
|
"domain": "coordination",
|
|
444
444
|
"sourcePath": "src/server/routes.ts",
|
|
445
|
-
"contentHash": "
|
|
445
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
446
446
|
"since": "2025-01-01"
|
|
447
447
|
},
|
|
448
448
|
"route-group:memory": {
|
|
@@ -450,7 +450,7 @@
|
|
|
450
450
|
"type": "route-group",
|
|
451
451
|
"domain": "memory",
|
|
452
452
|
"sourcePath": "src/server/routes.ts",
|
|
453
|
-
"contentHash": "
|
|
453
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
454
454
|
"since": "2025-01-01"
|
|
455
455
|
},
|
|
456
456
|
"route-group:semantic": {
|
|
@@ -458,7 +458,7 @@
|
|
|
458
458
|
"type": "route-group",
|
|
459
459
|
"domain": "memory",
|
|
460
460
|
"sourcePath": "src/server/routes.ts",
|
|
461
|
-
"contentHash": "
|
|
461
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
462
462
|
"since": "2025-01-01"
|
|
463
463
|
},
|
|
464
464
|
"route-group:status": {
|
|
@@ -466,7 +466,7 @@
|
|
|
466
466
|
"type": "route-group",
|
|
467
467
|
"domain": "monitoring",
|
|
468
468
|
"sourcePath": "src/server/routes.ts",
|
|
469
|
-
"contentHash": "
|
|
469
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
470
470
|
"since": "2025-01-01"
|
|
471
471
|
},
|
|
472
472
|
"route-group:capabilities": {
|
|
@@ -474,7 +474,7 @@
|
|
|
474
474
|
"type": "route-group",
|
|
475
475
|
"domain": "mapping",
|
|
476
476
|
"sourcePath": "src/server/routes.ts",
|
|
477
|
-
"contentHash": "
|
|
477
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
478
478
|
"since": "2025-01-01"
|
|
479
479
|
},
|
|
480
480
|
"route-group:project-map": {
|
|
@@ -482,7 +482,7 @@
|
|
|
482
482
|
"type": "route-group",
|
|
483
483
|
"domain": "mapping",
|
|
484
484
|
"sourcePath": "src/server/routes.ts",
|
|
485
|
-
"contentHash": "
|
|
485
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
486
486
|
"since": "2025-01-01"
|
|
487
487
|
},
|
|
488
488
|
"route-group:coherence": {
|
|
@@ -490,7 +490,7 @@
|
|
|
490
490
|
"type": "route-group",
|
|
491
491
|
"domain": "coherence",
|
|
492
492
|
"sourcePath": "src/server/routes.ts",
|
|
493
|
-
"contentHash": "
|
|
493
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
494
494
|
"since": "2025-01-01"
|
|
495
495
|
},
|
|
496
496
|
"route-group:topic-bindings": {
|
|
@@ -498,7 +498,7 @@
|
|
|
498
498
|
"type": "route-group",
|
|
499
499
|
"domain": "sessions",
|
|
500
500
|
"sourcePath": "src/server/routes.ts",
|
|
501
|
-
"contentHash": "
|
|
501
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
502
502
|
"since": "2025-01-01"
|
|
503
503
|
},
|
|
504
504
|
"route-group:context": {
|
|
@@ -506,7 +506,7 @@
|
|
|
506
506
|
"type": "route-group",
|
|
507
507
|
"domain": "context",
|
|
508
508
|
"sourcePath": "src/server/routes.ts",
|
|
509
|
-
"contentHash": "
|
|
509
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
510
510
|
"since": "2025-01-01"
|
|
511
511
|
},
|
|
512
512
|
"route-group:scope-coherence": {
|
|
@@ -514,7 +514,7 @@
|
|
|
514
514
|
"type": "route-group",
|
|
515
515
|
"domain": "coherence",
|
|
516
516
|
"sourcePath": "src/server/routes.ts",
|
|
517
|
-
"contentHash": "
|
|
517
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
518
518
|
"since": "2025-01-01"
|
|
519
519
|
},
|
|
520
520
|
"route-group:canonical-state": {
|
|
@@ -522,7 +522,7 @@
|
|
|
522
522
|
"type": "route-group",
|
|
523
523
|
"domain": "state",
|
|
524
524
|
"sourcePath": "src/server/routes.ts",
|
|
525
|
-
"contentHash": "
|
|
525
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
526
526
|
"since": "2025-01-01"
|
|
527
527
|
},
|
|
528
528
|
"route-group:ci": {
|
|
@@ -530,7 +530,7 @@
|
|
|
530
530
|
"type": "route-group",
|
|
531
531
|
"domain": "monitoring",
|
|
532
532
|
"sourcePath": "src/server/routes.ts",
|
|
533
|
-
"contentHash": "
|
|
533
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
534
534
|
"since": "2025-01-01"
|
|
535
535
|
},
|
|
536
536
|
"route-group:sessions": {
|
|
@@ -538,7 +538,7 @@
|
|
|
538
538
|
"type": "route-group",
|
|
539
539
|
"domain": "sessions",
|
|
540
540
|
"sourcePath": "src/server/routes.ts",
|
|
541
|
-
"contentHash": "
|
|
541
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
542
542
|
"since": "2025-01-01"
|
|
543
543
|
},
|
|
544
544
|
"route-group:jobs": {
|
|
@@ -546,7 +546,7 @@
|
|
|
546
546
|
"type": "route-group",
|
|
547
547
|
"domain": "scheduling",
|
|
548
548
|
"sourcePath": "src/server/routes.ts",
|
|
549
|
-
"contentHash": "
|
|
549
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
550
550
|
"since": "2025-01-01"
|
|
551
551
|
},
|
|
552
552
|
"route-group:skip-ledger": {
|
|
@@ -554,7 +554,7 @@
|
|
|
554
554
|
"type": "route-group",
|
|
555
555
|
"domain": "scheduling",
|
|
556
556
|
"sourcePath": "src/server/routes.ts",
|
|
557
|
-
"contentHash": "
|
|
557
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
558
558
|
"since": "2025-01-01"
|
|
559
559
|
},
|
|
560
560
|
"route-group:telegram": {
|
|
@@ -562,7 +562,7 @@
|
|
|
562
562
|
"type": "route-group",
|
|
563
563
|
"domain": "communication",
|
|
564
564
|
"sourcePath": "src/server/routes.ts",
|
|
565
|
-
"contentHash": "
|
|
565
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
566
566
|
"since": "2025-01-01"
|
|
567
567
|
},
|
|
568
568
|
"route-group:attention": {
|
|
@@ -570,7 +570,7 @@
|
|
|
570
570
|
"type": "route-group",
|
|
571
571
|
"domain": "communication",
|
|
572
572
|
"sourcePath": "src/server/routes.ts",
|
|
573
|
-
"contentHash": "
|
|
573
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
574
574
|
"since": "2025-01-01"
|
|
575
575
|
},
|
|
576
576
|
"route-group:relationships": {
|
|
@@ -578,7 +578,7 @@
|
|
|
578
578
|
"type": "route-group",
|
|
579
579
|
"domain": "relationships",
|
|
580
580
|
"sourcePath": "src/server/routes.ts",
|
|
581
|
-
"contentHash": "
|
|
581
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
582
582
|
"since": "2025-01-01"
|
|
583
583
|
},
|
|
584
584
|
"route-group:feedback": {
|
|
@@ -586,7 +586,7 @@
|
|
|
586
586
|
"type": "route-group",
|
|
587
587
|
"domain": "feedback",
|
|
588
588
|
"sourcePath": "src/server/routes.ts",
|
|
589
|
-
"contentHash": "
|
|
589
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
590
590
|
"since": "2025-01-01"
|
|
591
591
|
},
|
|
592
592
|
"route-group:updates": {
|
|
@@ -594,7 +594,7 @@
|
|
|
594
594
|
"type": "route-group",
|
|
595
595
|
"domain": "updates",
|
|
596
596
|
"sourcePath": "src/server/routes.ts",
|
|
597
|
-
"contentHash": "
|
|
597
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
598
598
|
"since": "2025-01-01"
|
|
599
599
|
},
|
|
600
600
|
"route-group:dispatches": {
|
|
@@ -602,7 +602,7 @@
|
|
|
602
602
|
"type": "route-group",
|
|
603
603
|
"domain": "dispatches",
|
|
604
604
|
"sourcePath": "src/server/routes.ts",
|
|
605
|
-
"contentHash": "
|
|
605
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
606
606
|
"since": "2025-01-01"
|
|
607
607
|
},
|
|
608
608
|
"route-group:quota": {
|
|
@@ -610,7 +610,7 @@
|
|
|
610
610
|
"type": "route-group",
|
|
611
611
|
"domain": "monitoring",
|
|
612
612
|
"sourcePath": "src/server/routes.ts",
|
|
613
|
-
"contentHash": "
|
|
613
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
614
614
|
"since": "2025-01-01"
|
|
615
615
|
},
|
|
616
616
|
"route-group:publishing": {
|
|
@@ -618,7 +618,7 @@
|
|
|
618
618
|
"type": "route-group",
|
|
619
619
|
"domain": "publishing",
|
|
620
620
|
"sourcePath": "src/server/routes.ts",
|
|
621
|
-
"contentHash": "
|
|
621
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
622
622
|
"since": "2025-01-01"
|
|
623
623
|
},
|
|
624
624
|
"route-group:private-views": {
|
|
@@ -626,7 +626,7 @@
|
|
|
626
626
|
"type": "route-group",
|
|
627
627
|
"domain": "publishing",
|
|
628
628
|
"sourcePath": "src/server/routes.ts",
|
|
629
|
-
"contentHash": "
|
|
629
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
630
630
|
"since": "2025-01-01"
|
|
631
631
|
},
|
|
632
632
|
"route-group:tunnel": {
|
|
@@ -634,7 +634,7 @@
|
|
|
634
634
|
"type": "route-group",
|
|
635
635
|
"domain": "networking",
|
|
636
636
|
"sourcePath": "src/server/routes.ts",
|
|
637
|
-
"contentHash": "
|
|
637
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
638
638
|
"since": "2025-01-01"
|
|
639
639
|
},
|
|
640
640
|
"route-group:events": {
|
|
@@ -642,7 +642,7 @@
|
|
|
642
642
|
"type": "route-group",
|
|
643
643
|
"domain": "networking",
|
|
644
644
|
"sourcePath": "src/server/routes.ts",
|
|
645
|
-
"contentHash": "
|
|
645
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
646
646
|
"since": "2025-01-01"
|
|
647
647
|
},
|
|
648
648
|
"route-group:evolution": {
|
|
@@ -650,7 +650,7 @@
|
|
|
650
650
|
"type": "route-group",
|
|
651
651
|
"domain": "evolution",
|
|
652
652
|
"sourcePath": "src/server/routes.ts",
|
|
653
|
-
"contentHash": "
|
|
653
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
654
654
|
"since": "2025-01-01"
|
|
655
655
|
},
|
|
656
656
|
"route-group:watchdog": {
|
|
@@ -658,7 +658,7 @@
|
|
|
658
658
|
"type": "route-group",
|
|
659
659
|
"domain": "monitoring",
|
|
660
660
|
"sourcePath": "src/server/routes.ts",
|
|
661
|
-
"contentHash": "
|
|
661
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
662
662
|
"since": "2025-01-01"
|
|
663
663
|
},
|
|
664
664
|
"route-group:topic-memory": {
|
|
@@ -666,7 +666,7 @@
|
|
|
666
666
|
"type": "route-group",
|
|
667
667
|
"domain": "memory",
|
|
668
668
|
"sourcePath": "src/server/routes.ts",
|
|
669
|
-
"contentHash": "
|
|
669
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
670
670
|
"since": "2025-01-01"
|
|
671
671
|
},
|
|
672
672
|
"route-group:state-sync": {
|
|
@@ -674,7 +674,7 @@
|
|
|
674
674
|
"type": "route-group",
|
|
675
675
|
"domain": "coordination",
|
|
676
676
|
"sourcePath": "src/server/routes.ts",
|
|
677
|
-
"contentHash": "
|
|
677
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
678
678
|
"since": "2025-01-01"
|
|
679
679
|
},
|
|
680
680
|
"route-group:intent": {
|
|
@@ -682,7 +682,7 @@
|
|
|
682
682
|
"type": "route-group",
|
|
683
683
|
"domain": "intent",
|
|
684
684
|
"sourcePath": "src/server/routes.ts",
|
|
685
|
-
"contentHash": "
|
|
685
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
686
686
|
"since": "2025-01-01"
|
|
687
687
|
},
|
|
688
688
|
"route-group:triage": {
|
|
@@ -690,7 +690,7 @@
|
|
|
690
690
|
"type": "route-group",
|
|
691
691
|
"domain": "safety",
|
|
692
692
|
"sourcePath": "src/server/routes.ts",
|
|
693
|
-
"contentHash": "
|
|
693
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
694
694
|
"since": "2025-01-01"
|
|
695
695
|
},
|
|
696
696
|
"route-group:operations": {
|
|
@@ -698,7 +698,7 @@
|
|
|
698
698
|
"type": "route-group",
|
|
699
699
|
"domain": "safety",
|
|
700
700
|
"sourcePath": "src/server/routes.ts",
|
|
701
|
-
"contentHash": "
|
|
701
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
702
702
|
"since": "2025-01-01"
|
|
703
703
|
},
|
|
704
704
|
"route-group:sentinel": {
|
|
@@ -706,7 +706,7 @@
|
|
|
706
706
|
"type": "route-group",
|
|
707
707
|
"domain": "safety",
|
|
708
708
|
"sourcePath": "src/server/routes.ts",
|
|
709
|
-
"contentHash": "
|
|
709
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
710
710
|
"since": "2025-01-01"
|
|
711
711
|
},
|
|
712
712
|
"route-group:trust": {
|
|
@@ -714,7 +714,7 @@
|
|
|
714
714
|
"type": "route-group",
|
|
715
715
|
"domain": "safety",
|
|
716
716
|
"sourcePath": "src/server/routes.ts",
|
|
717
|
-
"contentHash": "
|
|
717
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
718
718
|
"since": "2025-01-01"
|
|
719
719
|
},
|
|
720
720
|
"route-group:monitoring": {
|
|
@@ -722,7 +722,7 @@
|
|
|
722
722
|
"type": "route-group",
|
|
723
723
|
"domain": "monitoring",
|
|
724
724
|
"sourcePath": "src/server/routes.ts",
|
|
725
|
-
"contentHash": "
|
|
725
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
726
726
|
"since": "2025-01-01"
|
|
727
727
|
},
|
|
728
728
|
"route-group:commitments": {
|
|
@@ -730,7 +730,7 @@
|
|
|
730
730
|
"type": "route-group",
|
|
731
731
|
"domain": "commitments",
|
|
732
732
|
"sourcePath": "src/server/routes.ts",
|
|
733
|
-
"contentHash": "
|
|
733
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
734
734
|
"since": "2025-01-01"
|
|
735
735
|
},
|
|
736
736
|
"route-group:episodes": {
|
|
@@ -738,7 +738,7 @@
|
|
|
738
738
|
"type": "route-group",
|
|
739
739
|
"domain": "memory",
|
|
740
740
|
"sourcePath": "src/server/routes.ts",
|
|
741
|
-
"contentHash": "
|
|
741
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
742
742
|
"since": "2025-01-01"
|
|
743
743
|
},
|
|
744
744
|
"route-group:messages": {
|
|
@@ -746,7 +746,7 @@
|
|
|
746
746
|
"type": "route-group",
|
|
747
747
|
"domain": "coordination",
|
|
748
748
|
"sourcePath": "src/server/routes.ts",
|
|
749
|
-
"contentHash": "
|
|
749
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
750
750
|
"since": "2025-01-01"
|
|
751
751
|
},
|
|
752
752
|
"route-group:system-reviews": {
|
|
@@ -754,7 +754,7 @@
|
|
|
754
754
|
"type": "route-group",
|
|
755
755
|
"domain": "monitoring",
|
|
756
756
|
"sourcePath": "src/server/routes.ts",
|
|
757
|
-
"contentHash": "
|
|
757
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
758
758
|
"since": "2025-01-01"
|
|
759
759
|
},
|
|
760
760
|
"route-group:machine-mesh": {
|
|
@@ -770,7 +770,7 @@
|
|
|
770
770
|
"type": "route-group",
|
|
771
771
|
"domain": "security",
|
|
772
772
|
"sourcePath": "src/server/routes.ts",
|
|
773
|
-
"contentHash": "
|
|
773
|
+
"contentHash": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
774
774
|
"since": "2025-01-01"
|
|
775
775
|
},
|
|
776
776
|
"cli:init": {
|
|
@@ -1522,7 +1522,7 @@
|
|
|
1522
1522
|
"type": "subsystem",
|
|
1523
1523
|
"domain": "server",
|
|
1524
1524
|
"sourcePath": "src/server/AgentServer.ts",
|
|
1525
|
-
"contentHash": "
|
|
1525
|
+
"contentHash": "2f82d7f142123b9f586c355678de08656a4d3aa857a8d956f8a8bf46fb8a4701",
|
|
1526
1526
|
"since": "2025-01-01"
|
|
1527
1527
|
},
|
|
1528
1528
|
"subsystem:session-manager": {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
WS5.2 R7a — the per-account spend ceiling, lease-sliced and owned by the fenced single-writer, so multiple machines sharing one operator account can never collectively over-spend its quota (bounded by sum-of-leases, never N×ceiling — even under partition and across a lease-holder failover). New module `src/core/AccountFollowMeSpendSlice.ts` (pure/injectable): `SliceIssuer` (holder-side; refuses unless it holds the live fenced lease; delegates the sum-of-leases ceiling to the durable PR1 grant-ledger; epoch-stamps every slice), `SliceRenewalControl` (requester-side rate-cap + exponential backoff + per-(account,machine) coalescing + a P19 breaker that fails a machine closed to its OWN account when the holder is slow/partitioned — so the renewal RPC rate is O(per-account-cap), not O(N)), and `decideAccountUse` (the pure selection-time check — a borrowed account is used ONLY for a live, current-epoch, unexpired, positive-remaining slice; every uncertainty falls back to the machine's own account). A new operator-mandate-gated `slice-renew` mesh verb (its own deny-by-default RBAC case). The `AnthropicSubscriptionRouter` gains an optional consult hook (subscription-path only). Config knobs under `multiMachine.accountFollowMe.spendSlice`. Dark behind `multiMachine.accountFollowMe`.
|
|
9
|
+
|
|
10
|
+
**Scope (honest):** this delivers the spend-ceiling MECHANISM + the consultation; the router currently surfaces the decision to observability. The pool's account selection actually choosing own-vs-borrowed based on it is the enforcement integration that requires the (not-yet-live) borrowed-account-in-pool concept — so the consultation is inert today by construction (nothing is borrowed yet), leaving no reachable over-spend path unguarded. The enforcement wiring lands with that selection layer.
|
|
11
|
+
|
|
12
|
+
WS5.2 — one-dashboard cross-machine mandate delivery. Closes a real UX gap: authorizing an account-follow-me enrollment on another machine used to require a PIN-gated mandate issued on THAT machine's own dashboard (per-machine friction). Now the operator's SINGLE dashboard does it: a new PIN-gated `POST /mandate/issue-for-machine` issues the mandate locally and, for a remote target, packages it with the operator machine's Ed25519 signature (the already-approved ws52 R4a bridge) and delivers it over a new `account-follow-me-mandate-deliver` mesh verb. The target authenticates the mesh sender (existing Ed25519 envelope verification → the registered operator-machine identity), R4a-verifies the mandate's issuance signature against that registered key (a name in the payload is never trusted — Know Your Principal), checks the exact bounds (account-follow-me / this machine / re-mint), and persists it to a new `DeliveredMandateStore`. The enroll-start route then consults the delivered mandate (re-verifying the signature at point-of-use) when the local gate has none — both paths fail-closed. The core MandateGate authorship model is untouched. Dark behind `multiMachine.accountFollowMe`.
|
|
13
|
+
|
|
14
|
+
## What to Tell Your User
|
|
15
|
+
|
|
16
|
+
Nothing to do — this ships off by default. It's the safety cap that lets several of your machines share one subscription account without them collectively blowing past that account's quota: each machine gets a metered slice of the account's allowance from one coordinating machine, and if it can't reach that coordinator it quietly uses its own account instead of overspending the shared one.
|
|
17
|
+
|
|
18
|
+
You now manage everything from your ONE dashboard — no more "open the other machine's dashboard." When you approve a machine to use one of your accounts, you do it once on your single dashboard with your PIN; your approval is cryptographically signed and delivered to that machine, which verifies it really came from you before acting. You never touch the other machine. (Off by default while the broader account-sharing feature is dark.)
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
Cross-machine per-account spend ceiling (dark): lease-sliced sub-budgets issued by the fenced single-writer, bounded by sum-of-leases under partition + failover, with an operator-mandate-gated renewal mesh verb and fail-closed-to-own-account on every uncertainty. No user-facing surface is live in this release.
|
|
23
|
+
|
|
24
|
+
One-dashboard cross-machine mandate authorization (dark): issue + deliver an account-follow-me mandate to any of your machines from your single dashboard; the target verifies it via the R4a operator signature and honors it only for its exact bounds. New PIN-gated `POST /mandate/issue-for-machine` + the `account-follow-me-mandate-deliver` mesh verb. No user-facing surface is live until `multiMachine.accountFollowMe` is enabled.
|
|
25
|
+
|
|
26
|
+
## Evidence
|
|
27
|
+
|
|
28
|
+
- 59+ tests across `account-followme-spend-slice` (fenced issuance, non-holder refusal, sum-of-leases/Nth-VM refusal, failover re-derivation with no double-allocation, coalescing, rate-cap+backoff, refusal-vs-failure breaker distinction, breaker open/cooldown/reset, all `decideAccountUse` branches), `MeshRpc` (slice-renew deny-by-default RBAC: unwired/rogue/mandated), and `anthropic-subscription-router` (unwired byte-identical, consult on subscription paths only). `tsc --noEmit` clean. Dark-gate lint golden-map updated for the new config block (24/24).
|
|
29
|
+
- Side-effects review + mandatory independent second-pass security review (concurred on all 7 audit points: sum-of-leases bound, failover no-double-allocation, fail-closed-to-own everywhere, deny-by-default RBAC, O(per-account-cap) control plane, dark-by-default, no fail-open).
|
|
30
|
+
- Spec: `docs/specs/ws52-account-follow-me-security.md` R7a/R7/S5 (converged, approved).
|
|
31
|
+
|
|
32
|
+
- 122 tests across 11 files (delivery store + accept/trust path: valid R4a → stored+honored; bad signature / untrusted key / issuer≠sender / wrong-target / wrong-mechanism / non-follow-me → refused & nothing persisted; mesh-verb RBAC deny-by-default; issue-for-machine PIN-gated + dark→503 + honest 502 on delivery failure + local-target unchanged; enroll-start honoring a delivered+verified mandate vs 403 without one; dashboard routing). `tsc --noEmit` clean.
|
|
33
|
+
- Side-effects review + mandatory independent second-pass security review (concurred on all 8 points: trust anchor = authenticated sender never payload, R4a load-bearing fail-closed, exact-bounds double-gated no-replay, enroll-start additive fail-closed with point-of-use re-verify, PIN-gated issuance / no self-issue, deny-by-default mesh RBAC, dark-by-default, no fail-open).
|
|
34
|
+
- Spec: `docs/specs/ws52-account-follow-me-security.md` R4a/R1/R6a (converged, approved) — wires the spec's R4a cross-machine mandate bridge.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Side-Effects Review — WS5.2 R7a: lease-sliced per-account spend ceiling
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `ws52-account-follow-me-r7a-spend-slice`
|
|
4
|
+
**Date:** `2026-06-17`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `pending (HIGHEST-risk: cross-machine money/quota ceiling under partition + a new operator-mandate-gated mesh verb)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
WS5.2 R7a. A per-account spend ceiling, lease-sliced, owned by the fenced single-writer (the `FencedLease` holder), so N machines sharing one operator account can never collectively over-spend its quota — bounded by sum-of-leases, never N×ceiling, even under partition and across lease-holder failover. New module `src/core/AccountFollowMeSpendSlice.ts` (pure/injectable): `SliceIssuer` (holder-side — refuses `not-lease-holder`, stamps the current lease epoch, delegates the sum-of-leases ceiling to the existing durable `AccountFollowMeGrantLedger`); `SliceRenewalControl` (requester-side rate-cap + exponential backoff + per-(account,machine) coalescing + P19 breaker → fail-closed-to-own-account on a slow/partitioned holder); `decideAccountUse` (pure selection-time consultation — borrowed account used ONLY for a live, current-epoch, unexpired, remaining>0 slice; every uncertainty → own account). A new operator-mandate-gated `slice-renew` mesh verb in `MeshRpc.ts` (its own deny-by-default `checkCommandRBAC` case via an injected `authorizeSliceRenew` seam). `AnthropicSubscriptionRouter` gains optional `consultSlice`/`onSliceConsult` hooks invoked only on the subscription-pool selection path (no-op when unwired). Config knobs under `multiMachine.accountFollowMe.spendSlice`. Dark behind `multiMachine.accountFollowMe`; default behavior byte-identical when the hook is unwired/off.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `slice-renew` mesh verb RBAC — **add** — deny-by-default; only the operator-mandate-authorized requester (via `authorizeSliceRenew`) may request a slice; absent seam → deny.
|
|
15
|
+
- `SliceIssuer.issueForRenew` — **add** — only the live `FencedLease` holder issues; ceiling enforced by the durable ledger; epoch-stamped.
|
|
16
|
+
- `decideAccountUse` — **add** — selection-time: borrowed vs own account; fail-closed-to-own on every uncertainty.
|
|
17
|
+
- `SliceRenewalControl` — **add** — the control-plane bound (rate-cap/coalesce/breaker) → O(per-account-cap) renewal RPCs.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Over-block
|
|
22
|
+
|
|
23
|
+
The conservative direction is intentional: when a slice is unknown/stale/expired/exhausted, or the holder is slow/partitioned, a machine "over-blocks" by falling back to its OWN account rather than using the borrowed one. This is the REQUIRED safe direction (never overspend a borrowed money/quota credential). It cannot wrongly deny the machine's own account (fallback is always available). No legitimate over-block of a healthy borrowed slice (a live current-epoch slice with budget is used).
|
|
24
|
+
|
|
25
|
+
## 2. Under-block
|
|
26
|
+
|
|
27
|
+
The ceiling is enforced at slice ISSUANCE (the ledger refuses `outstanding + amount > ceiling`) and at selection (a machine without budget falls back). It does NOT meter per-call token spend WITHIN a slice — a slice is a pre-allocated sub-budget; intra-slice accounting is the slice's granularity (by design — the spec's lease-slice model). A compromised holder could in principle over-issue, but the holder is the fenced single-writer (the same trust root as who-is-awake) and the ledger still caps the durable sum; a non-holder cannot issue at all.
|
|
28
|
+
|
|
29
|
+
## 3. Level-of-abstraction fit
|
|
30
|
+
|
|
31
|
+
Correct layers: issuance authority on the FencedLease holder (the existing single-writer), durable accounting in the existing GrantLedger (reused, not rebuilt), the renewal control-plane as a pure injectable state machine, the consultation as a pure function the router calls. The mesh verb sits in MeshRpc with the other commands. No logic duplicated; the ledger's sum-of-leases + epoch-fencing is reused as-is.
|
|
32
|
+
|
|
33
|
+
## 4. Signal vs authority compliance
|
|
34
|
+
|
|
35
|
+
The authority here (issue a spend slice) is held by the fenced single-writer + gated by the operator mandate (deny-by-default) — appropriate, not a brittle heuristic. The consultation (`decideAccountUse`) is a deterministic pure function (field checks on a slice), fail-closed. The breaker is a signal (consecutive transport failures → open) that only ever makes a machine MORE conservative (fall back to own account). No brittle blocking authority over user actions. Reference `docs/signal-vs-authority.md`: operator-mandate + fenced-single-writer authority, deny-by-default, is correctly-placed.
|
|
36
|
+
|
|
37
|
+
## 5. Interactions
|
|
38
|
+
|
|
39
|
+
Reuses `AccountFollowMeGrantLedger` (PR1) for the durable sum-of-leases bound + epoch-fenced consume — failover re-derivation is "for free" because `SliceIssuer` holds no in-memory slice state (reads the durable store live; a new holder over the same store sees the committed set). The `not-lease-holder` guard prevents a stale ex-holder from issuing during a handoff. The breaker distinguishes grant REFUSALS (`would-exceed-ceiling` — the holder answered; back off but don't trip) from transport FAILURES (trip the breaker) — so a healthy-but-full account doesn't open the breaker. The router hook runs only on the subscription path (never sdk-credit), so it can't perturb SDK routing.
|
|
40
|
+
|
|
41
|
+
## 6. External surfaces
|
|
42
|
+
|
|
43
|
+
One new mesh verb (`slice-renew`), deny-by-default, dark behind the flag. New config block (`spendSlice`, additive, defaulted). No new HTTP route. When `multiMachine.accountFollowMe` is off (or the router hook unwired — the default), behavior is byte-identical: the consult hook is never invoked. Other agents see the new verb only if they're mandated peers in a follow-me pool.
|
|
44
|
+
|
|
45
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
46
|
+
|
|
47
|
+
This IS a multi-machine coherence mechanism by construction. The single source of truth is the fenced-lease holder + the durable grant-ledger; slices are epoch-stamped so a failover voids stale-epoch slices and the new holder re-derives the outstanding set from the durable store before issuing — the sum-of-leases bound holds across the handoff with no double-allocation. Under partition, a VM that can't reach the holder fails closed to its own account (bounded, never unbounded overshoot). The renewal control plane is O(per-account-cap), not O(N), so it can't storm the holder. A single-machine agent is a no-op (no peers, the flag is off).
|
|
48
|
+
|
|
49
|
+
## 8. Rollback cost
|
|
50
|
+
|
|
51
|
+
Low. Entirely dark behind `multiMachine.accountFollowMe` + the unwired router hook → no live effect by default. New module + additive config + one mesh verb. Revert is a single-commit back-out; no migration, no persisted-schema change (the slice records live in the existing grant-ledger store, which already shipped in PR1).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Scope note (honest, not a hidden deferral)
|
|
56
|
+
|
|
57
|
+
R7a delivers the spend-ceiling MECHANISM end-to-end: the fenced-single-writer slice issuer (ledger-backed sum-of-leases ceiling + epoch-fenced failover re-derivation), the requester-side renewal control plane (rate-cap + coalescing + P19 breaker → O(per-account-cap)), the operator-mandate-gated `slice-renew` mesh verb, the pure `decideAccountUse` consultation (fail-closed-to-own on every uncertainty), AND the router hook that invokes it. The router currently SURFACES the decision to observability (`onSliceConsult`); the pool's account-selection actually CHOOSING own-vs-borrowed based on that decision is the enforcement integration that belongs to — and requires — the borrowed-account-in-pool concept, which is not yet live (enrollment adds accounts as normal local accounts; nothing is marked `isBorrowedAccount` yet). So the consultation is inert today by construction (every account → `own`), meaning there is NO reachable over-spend path that this layer leaves unguarded. This is the correct decomposition for a dark mechanism, not a deferral of reachable behavior; the enforcement wiring lands with the borrowed-account selection layer.
|
|
58
|
+
|
|
59
|
+
## Second-pass review
|
|
60
|
+
|
|
61
|
+
**Concur with the review** (security substance) — the independent reviewer verified all 7 points directly:
|
|
62
|
+
1. **Sum-of-leases bound holds** — `SliceIssuer.issueForRenew` delegates to the ledger's `issue()` (re-reads outstanding from the durable store, refuses `outstanding + amount > ceiling`); `SliceIssuer` holds ZERO in-memory accounting state; the `GrantStore` is synchronous so no interleave past the ceiling (structural invariant: keep the store synchronous). `outstandingFor` conservatively over-counts (safe direction).
|
|
63
|
+
2. **Failover / no double-allocation** — `holdsLease()` blocks a stale ex-holder; the new holder re-derives from the same durable store; epoch-stamped slices are void on stale epoch. Tested.
|
|
64
|
+
3. **Fail-closed-to-own on every uncertainty** — `decideAccountUse` returns `own` for flag-off / not-borrowed / no-slice / stale-epoch / expired / `!(remaining>0)` (0, negative, NaN). `borrowed` only via a live current-epoch unexpired positive-remaining slice.
|
|
65
|
+
4. **slice-renew RBAC deny-by-default** — own `checkCommandRBAC` case; `authorizeSliceRenew?.(...) ?? false` → absent seam OR false → 403; not the any-peer read class.
|
|
66
|
+
5. **Control plane O(per-account-cap)** — coalescing + rate-cap + exponential backoff + P19 breaker on transport `failed` only; grant `refused` (would-exceed-ceiling) backs off but does NOT trip the breaker.
|
|
67
|
+
6. **Dark by default / byte-identical when off** — `consultSlice` no-ops when unwired; subscription-path only, never sdk-credit. (Observe-only in this layer — see Scope note above.)
|
|
68
|
+
7. **No fail-open** — no catch/`||true`/default-allow; every positive outcome follows explicit guards.
|
|
69
|
+
|
|
70
|
+
The reviewer's one CONCERN was the dark-gate lint golden-map line shift from the new `spendSlice` config block — **FIXED**: the 7 shifted entries updated to the attributor's values (924/949/978/1147/1269/1314/1339); `lint-dev-agent-dark-gate.test.ts` now 24/24 green. tsc EXIT=0; the 3 R7a suites 59 passed.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Side-Effects Review — WS5.2 one-dashboard cross-machine mandate delivery
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `ws52-one-dashboard-mandate-delivery`
|
|
4
|
+
**Date:** `2026-06-17`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `concurred (HIGH-stakes: cross-machine authorization delivery)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Operator feedback (topic 13481): "there should only be ONE dashboard for the agent, not one per machine." Today an account-follow-me enrollment on a TARGET machine needs a PIN-gated mandate ISSUED on that target's OWN dashboard — the per-machine friction the operator rejects. This change lets the operator's SINGLE dashboard issue + deliver the mandate to the target, verified there via the already-approved ws52 R4a bridge. New `POST /mandate/issue-for-machine` (PIN-gated, same auth as `/mandate/issue`): issues the mandate locally, then for a REMOTE target packages it (`packageMandateForDelivery`, signed with this operator machine's Ed25519 identity key) and dispatches it over a new `account-follow-me-mandate-deliver` mesh verb. The target's handler authenticates the mesh `sender` (existing Ed25519 envelope verification → the registered operator-machine identity), resolves that sender's REGISTERED key, R4a-verifies the mandate's issuance signature against it (`acceptMandateDelivery` → `acceptDeliveredMandate`, with `expectedIssuer === authenticated sender`), checks exact bounds (account-follow-me / this machine / re-mint), and only then persists it to a new `DeliveredMandateStore`. The enroll-start route (`POST /subscription-pool/follow-me/enroll/start`) now, when the LOCAL gate denies, ALSO consults the delivered mandate — re-verifying the R4a signature at point-of-use and re-checking bounds — both paths fail-closed (403). Files: `AccountFollowMeMandateDelivery.ts` + `DeliveredMandateStore.ts` (new), `MeshRpc.ts`, `routes.ts`, `AgentServer.ts`, `commands/server.ts`, `dashboard/mandates.js`. Dark behind `multiMachine.accountFollowMe`.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `account-follow-me-mandate-deliver` mesh verb handler / `acceptMandateDelivery` — **add** — the cross-machine authorization-acceptance decision: a delivered mandate is trusted ONLY via R4a verification against the authenticated sender's registered key + exact bounds. Deny-by-default.
|
|
15
|
+
- `POST /mandate/issue-for-machine` — **add** — PIN-gated issuance + remote delivery (agent cannot self-issue).
|
|
16
|
+
- enroll-start delivered-mandate consultation — **modify** — an ADDITIONAL fail-closed authorization source alongside the unchanged local gate.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Over-block
|
|
21
|
+
Conservative by design: any uncertainty (no registered operator key for the sender, signature fails, bounds mismatch, target≠this machine, feature off) denies. The operator's own PIN-gated issuance is unchanged, so the operator is never wrongly blocked from authorizing; a delivery that can't be verified simply isn't honored (the operator retries / the honest 502 surfaces). No legitimate authorized enrollment is rejected.
|
|
22
|
+
|
|
23
|
+
## 2. Under-block
|
|
24
|
+
The delivered mandate authorizes ONLY its exact bounds (account-follow-me, specific accountId, this targetMachineId, re-mint). It cannot be replayed for another account/machine. It does not expand what a mandate authorizes — it only changes WHERE the operator can issue it from (one dashboard) and HOW it reaches the target (signed mesh delivery). The downstream S7 email-gate + the enrollment flow are unchanged.
|
|
25
|
+
|
|
26
|
+
## 3. Level-of-abstraction fit
|
|
27
|
+
Correct: issuance stays in the PIN-gated mandate route; delivery rides the existing authenticated `meshRpcDispatcher` (same pipe as remote-close); acceptance uses the already-approved ws52 R4a bridge (`acceptDeliveredMandate`); the core `MandateGate` authorship model is UNTOUCHED (delivered mandates use the R4a path, scoped to the account-follow-me enroll-start consumer — not a change to how every mandate is trusted). This is the minimal-blast-radius placement.
|
|
28
|
+
|
|
29
|
+
## 4. Signal vs authority compliance
|
|
30
|
+
The authority is the operator's PIN-gated mandate (unchanged) + the R4a cryptographic binding to the registered operator machine. The agent cannot self-issue (bearer token insufficient — PIN required). The mesh verb's real gate is the signature re-verification, not a coarse role check. Deterministic + fail-closed; reference `docs/signal-vs-authority.md`.
|
|
31
|
+
|
|
32
|
+
## 5. Interactions
|
|
33
|
+
The enroll-start consultation is ADDITIVE — the existing local-mandate path is byte-unchanged; the delivered path is reached only when the local gate denies, and both deny-by-default. `verifyDeliveredMandate` re-verifies at point-of-use (never trusts a stored flag). No change to the core MandateGate, so no other mandate consumer is affected. The delivery RPC failure surfaces as an honest 502 (retryable), never a silent stuck issuance.
|
|
34
|
+
|
|
35
|
+
## 6. External surfaces
|
|
36
|
+
One new PIN-gated route + one new mesh verb (deny-by-default, signature-gated) + a dashboard change routing account-follow-me issuance through the new route. All dark behind `multiMachine.accountFollowMe` (503/refuse when off). The mesh verb is only honored from an authenticated registered peer whose R4a signature verifies — no new attack surface for an unauthenticated party.
|
|
37
|
+
|
|
38
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
39
|
+
This change EXISTS to fix a cross-machine coherence defect: the per-machine dashboard/mandate seam. It is replicated-by-delivery: the operator's single machine is the issuance authority; the signed mandate is delivered to + verified on the target; the target holds it locally (in DeliveredMandateStore) for its own enroll-start. Trust is grounded in the registered MachineIdentity store (Know Your Principal — the authenticated mesh sender, never a payload name). Single-machine agents never deliver (local target path unchanged) — no-op.
|
|
40
|
+
|
|
41
|
+
## 8. Rollback cost
|
|
42
|
+
Low. Dark by default; new files + additive route/verb; the core MandateGate is untouched so reverting cannot destabilize existing mandate behavior. Single-commit back-out; no migration (DeliveredMandateStore is new + only populated when the feature is live).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 6b. Operator-Surface Quality (dashboard change)
|
|
47
|
+
|
|
48
|
+
The change touches `dashboard/mandates.js` (an operator surface), so per the Operator-Surface Quality standard:
|
|
49
|
+
|
|
50
|
+
1. **Right action surfaced prominently / clear next step:** The change makes the EXISTING mandate issue form work for a remote target — when the operator approves an `account-follow-me` authority, it routes through `/mandate/issue-for-machine` and shows a plain-language outcome ("delivered to <machine>" / "issued locally" / a retryable "couldn't reach <machine>" on a 502). The operator's action (approve with PIN) is unchanged and primary; the delivery is automatic. This REMOVES a confusing dead-end (the old "open that machine's dashboard to approve" note) — the exact operator-surface defect this change fixes.
|
|
51
|
+
2. **No internals as primary content:** The operator sees machine NICKNAMES and a plain outcome sentence, not fingerprints/mesh internals (those remain in the support/reference line only). The cross-machine delivery + R4a verification happen invisibly.
|
|
52
|
+
3. **Destructive actions de-emphasized:** This surface issues an authorization (not a destructive action); revoke remains the separate, clearly-labeled control. Nothing destructive is added or made prominent.
|
|
53
|
+
4. **Plain language at phone width:** The new notices are short plain sentences ("Approved — delivered to Mac Mini"), not JSON or jargon, and reuse the existing mobile-responsive mandate-card layout. No new wide/desktop-only element.
|
|
54
|
+
|
|
55
|
+
(Note: a dedicated pre-filled follow-me picker reading the deep-link `?account=&target=&mechanism=` params is a nice-to-have not built here; the functional path works via the existing form. Tracked as a polish follow-up <!-- tracked: topic-13481 -->.)
|
|
56
|
+
|
|
57
|
+
## Agent Proposes, Operator Approves
|
|
58
|
+
|
|
59
|
+
The operator approves a SERVER-AUTHORED request built from STRUCTURED DATA — never agent-supplied free-text:
|
|
60
|
+
- The mandate's authority is a structured `CoordinationMandate` (bounds: `accountId`, `targetMachineId`, `mechanism:'re-mint'`, `action:'account-follow-me'`, `expiresAt`) constructed server-side by `MandateStore.issue()` from typed fields. The operator approves it with their dashboard PIN (`checkMandatePin`) — the agent's bearer token cannot issue/widen it (requester ≠ authorizer preserved).
|
|
61
|
+
- The cross-machine delivery carries that SAME canonical structured mandate; the R4a signature is over the server-canonicalized bytes (`canonicalMandate`), and the target re-derives the bounds from the structured mandate (`readFollowMeBounds`) — it never parses or trusts agent/operator free-text. A name in the payload is never authority (Know Your Principal); only the structured bounds + the R4a signature against the registered operator key are.
|
|
62
|
+
- The dashboard surfaces the structured authority (account + target machine nickname + the fixed action slug) for approval; it does not let the agent inject free-text that becomes the approved authority. The operator approves a typed, server-authored grant, full stop.
|
|
63
|
+
|
|
64
|
+
## Second-pass review
|
|
65
|
+
|
|
66
|
+
**Concur with the review.** Independent audit verified all 8 points against the actual source (tsc EXIT=0; 47/47 tests):
|
|
67
|
+
1. **Trust anchor = authenticated sender, never payload** — handler resolves `operatorPem = meshIdMgr.getSigningPublicKeyPem(sender)` (same registered-identity source `verifyEnvelope` uses); `acceptMandateDelivery` passes `expectedOperatorMachineFingerprint: sender`; `CrossMachineMandate` rejects `issuerFingerprint !== expectedIssuer`. No payload field is ever the anchor.
|
|
68
|
+
2. **R4a load-bearing + fail-closed** — no operator key → refuse; sig fail → refuse; issuer≠sender → refuse; `store.put` strictly after all checks. Unverified mandate never persisted/honored.
|
|
69
|
+
3. **Exact-bounds, no replay** — double-gated (accept-time + enroll-start point-of-use): accountId + targetMachineId===this-machine + mechanism=re-mint. A/X cannot be replayed for B/Y.
|
|
70
|
+
4. **enroll-start additive + fail-closed** — local path unchanged; delivered path reached only on local-deny; `verifyDeliveredMandate` re-verifies R4a (not a stored flag); no match → 403.
|
|
71
|
+
5. **issue-for-machine PIN-gated** — `checkMandatePin` same as `/mandate/issue`; bearer alone insufficient; local-target unchanged.
|
|
72
|
+
6. **Mesh RBAC deny-by-default** — own case; `authorizeMandateDeliver ?? false`; real authority = signature re-verify (rogue registered peer still can't deliver an unsigned mandate).
|
|
73
|
+
7. **Dark by default** — route 503, verb refused, accept `feature-disabled` when off; single-machine no-op.
|
|
74
|
+
8. **No fail-open** — terminal `accepted:true` only after all refusals; the only `@silent-fallback-ok` catches fail toward denial (`ok:false` / `[]`).
|