exochain-wasm 0.2.0-beta

ExoChain governance engine — WebAssembly bindings for Node.js
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
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
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
// Copyright 2026 Exochain Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

//! ExoChain WASM Bindings
//!
//! Exposes the full ExoChain governance engine to JavaScript/Node.js via WebAssembly.
//! Covers 14 crates: core, identity, consent, authority, gatekeeper, governance,
//! escalation, legal, dag, proofs, api, tenant, decision-forum, and messaging.

#![cfg_attr(test, allow(clippy::expect_used, clippy::unwrap_used))]

pub mod authority_bindings;
pub mod avc_bindings;
pub mod catapult_bindings;
pub mod consent_bindings;
pub mod core_bindings;
pub mod decision_forum_bindings;
pub mod economy_bindings;
pub mod escalation_bindings;
pub mod gatekeeper_bindings;
pub mod governance_bindings;
pub mod identity_bindings;
pub mod legal_bindings;
pub mod messaging_bindings;
mod serde_bridge;

// Flat re-exports so integration tests and downstream rlib consumers can
// access all WASM bindings as `exochain_wasm::wasm_*` without module prefixes.
pub use authority_bindings::*;
pub use avc_bindings::*;
pub use catapult_bindings::*;
pub use consent_bindings::*;
pub use core_bindings::*;
pub use decision_forum_bindings::*;
pub use economy_bindings::*;
pub use escalation_bindings::*;
pub use gatekeeper_bindings::*;
pub use governance_bindings::*;
pub use identity_bindings::*;
pub use legal_bindings::*;
pub use messaging_bindings::*;

#[cfg(test)]
mod source_guard_tests {
    #[test]
    fn wasm_bridge_uses_deterministic_collections() {
        let binding_sources = [
            (
                "authority_bindings.rs",
                include_str!("authority_bindings.rs"),
            ),
            (
                "governance_bindings.rs",
                include_str!("governance_bindings.rs"),
            ),
            ("economy_bindings.rs", include_str!("economy_bindings.rs")),
            ("avc_bindings.rs", include_str!("avc_bindings.rs")),
        ];

        for (path, source) in binding_sources {
            assert!(
                !source.contains("HashMap"),
                "{path} must use deterministic BTreeMap-style collections at the WASM boundary"
            );
            assert!(
                !source.contains("HashSet"),
                "{path} must use deterministic BTreeSet-style collections at the WASM boundary"
            );
        }
    }

    #[test]
    fn wasm_consent_bridge_requires_caller_supplied_time() {
        let source = include_str!("consent_bindings.rs");
        let forbidden = [
            format!("{}{}", "Timestamp::", "now_utc()"),
            format!("{}{}", "Uuid::", "new_v4()"),
            "HybridClock::new()".to_string(),
        ];

        for pattern in forbidden {
            assert!(
                !source.contains(&pattern),
                "consent WASM bindings must receive caller-supplied IDs and HLC timestamps"
            );
        }
    }

    #[test]
    fn wasm_consent_termination_refuses_unsigned_actor_did_bridge() {
        let source = include_str!("consent_bindings.rs");
        let termination = source
            .split("pub fn wasm_terminate_bailment(")
            .nth(1)
            .and_then(|section| {
                section
                    .split("pub fn wasm_terminate_bailment_signed(")
                    .next()
            })
            .expect("wasm_terminate_bailment source");
        let signed_termination = source
            .split("pub fn wasm_terminate_bailment_signed(")
            .nth(1)
            .expect("wasm_terminate_bailment_signed source");

        assert!(
            termination.contains("unsigned bailment termination is disabled"),
            "legacy WASM bailment termination must fail closed instead of trusting actor_did"
        );
        assert!(
            !termination.contains("exo_consent::bailment::terminate(&mut bailment, &actor)"),
            "legacy WASM bailment termination must not reach the core state transition"
        );
        assert!(
            source.contains("pub fn wasm_bailment_termination_payload("),
            "WASM consent bridge must expose the canonical termination payload for external signing"
        );
        assert!(
            source.contains("pub fn wasm_terminate_bailment_signed("),
            "WASM consent bridge must keep the signed termination entrypoint fail-closed"
        );
        assert!(
            signed_termination.contains("untrusted_wasm_bailment_termination_error"),
            "WASM signed termination must fail closed instead of trusting caller-supplied key material"
        );
        assert!(
            !signed_termination.contains("terminate_verified"),
            "WASM signed termination must not call core termination without trusted DID resolution"
        );
    }

    #[test]
    fn wasm_consent_acceptance_refuses_caller_supplied_bailee_keys() {
        let source = include_str!("consent_bindings.rs");
        let acceptance = source
            .split("pub fn wasm_accept_bailment(")
            .nth(1)
            .and_then(|section| {
                section
                    .split("pub fn wasm_bailment_signing_payload(")
                    .next()
            })
            .expect("wasm_accept_bailment source");

        assert!(
            acceptance.contains("untrusted_wasm_bailment_acceptance_error"),
            "WASM bailment acceptance must fail closed without trusted DID resolution"
        );
        assert!(
            !acceptance.contains("exo_consent::bailment::accept("),
            "WASM bailment acceptance must not call core accept without trusted DID resolution"
        );
    }

    #[test]
    fn wasm_governance_bridge_requires_caller_supplied_metadata() {
        let source = include_str!("governance_bindings.rs");
        let forbidden = [
            format!("{}{}", "Timestamp::", "now_utc()"),
            format!("{}{}", "Uuid::", "new_v4()"),
            "HybridClock::new()".to_string(),
        ];

        for pattern in forbidden {
            assert!(
                !source.contains(&pattern),
                "governance WASM bindings must receive caller-supplied IDs and HLC timestamps"
            );
        }
    }

    #[test]
    fn wasm_governance_close_requires_trusted_runtime_adapter() {
        let source = include_str!("governance_bindings.rs");
        let production = source
            .split("#[cfg(test)]")
            .next()
            .expect("production section");
        assert!(
            production
                .contains("verified deliberation closure requires a trusted core runtime adapter"),
            "public WASM deliberation close must fail closed without a trusted runtime adapter"
        );
        assert!(
            !production.contains("deliberation::close(&mut delib"),
            "WASM deliberation close must not call the structural-only close path"
        );
        assert!(
            !production.contains("close_verified(&mut delib, &policy, &resolver)"),
            "public WASM deliberation close must not trust caller-supplied signer keys and roles"
        );
    }

    #[test]
    fn wasm_governance_clearance_requires_caller_supplied_registry() {
        let source = include_str!("governance_bindings.rs");
        assert!(
            source.contains("registry_json"),
            "WASM clearance checks must accept caller-supplied clearance registry data"
        );
        assert!(
            !source.contains("ClearanceLevel::Governor"),
            "WASM clearance checks must not fabricate Governor clearance"
        );
    }

    #[test]
    fn wasm_governance_bridge_bounds_untrusted_collection_inputs() {
        let source = include_str!("governance_bindings.rs");
        for required in [
            "MAX_WASM_CLEARANCE_REGISTRY_ENTRIES",
            "MAX_WASM_CONFLICT_DECLARATIONS",
            "MAX_WASM_AUDIT_ENTRIES",
            "MAX_WASM_DELIBERATION_PARTICIPANTS",
            "MAX_WASM_INDEPENDENCE_ACTORS",
            "MAX_WASM_REGISTRY_RELATIONSHIPS",
            "MAX_WASM_COORDINATION_ACTIONS",
            "MAX_WASM_PROPOSAL_BYTES",
            "MAX_WASM_CHALLENGE_EVIDENCE_BYTES",
            "parse_bounded_vec",
        ] {
            assert!(
                source.contains(required),
                "governance WASM boundary must define and use {required}"
            );
        }

        for forbidden in [
            "let key_pairs: Vec<(String, String)> = from_json_str(public_keys_json)?;",
            "let entries: Vec<WasmClearanceRegistryEntry> = from_json_str(registry_json)?;",
            "let approvals: Vec<exo_governance::quorum::Approval> = from_json_str(approvals_json)?;",
            "let entries: Vec<exo_governance::audit::AuditEntry> = from_json_str(entries_json)?;",
            "let did_strs: Vec<String> = from_json_str(participants_json)?;",
            "let did_strs: Vec<String> = from_json_str(actors_json)?;",
            "let actions: Vec<exo_governance::crosscheck::TimestampedAction> = from_json_str(actions_json)?;",
        ] {
            assert!(
                !source.contains(forbidden),
                "governance WASM boundary must not deserialize untrusted arrays without a count bound: {forbidden}"
            );
        }
    }

    #[test]
    fn wasm_non_governance_vec_inputs_use_explicit_count_bounds() {
        let required = [
            (
                "authority_bindings.rs",
                include_str!("authority_bindings.rs"),
                "MAX_WASM_AUTHORITY_LINKS",
            ),
            (
                "authority_bindings.rs",
                include_str!("authority_bindings.rs"),
                "AUTHORITY_CHAIN_TRUSTED_ADAPTER_REQUIRED",
            ),
            (
                "authority_bindings.rs",
                include_str!("authority_bindings.rs"),
                "AUTHORITY_CHAIN_CALLER_KEYS_REJECTED",
            ),
            (
                "identity_bindings.rs",
                include_str!("identity_bindings.rs"),
                "MAX_WASM_SHAMIR_SHARES",
            ),
            (
                "escalation_bindings.rs",
                include_str!("escalation_bindings.rs"),
                "MAX_WASM_DETECTION_SIGNALS",
            ),
            (
                "escalation_bindings.rs",
                include_str!("escalation_bindings.rs"),
                "MAX_WASM_FEEDBACK_ENTRIES",
            ),
            (
                "escalation_bindings.rs",
                include_str!("escalation_bindings.rs"),
                "MAX_WASM_ESCALATION_CASES",
            ),
            (
                "messaging_bindings.rs",
                include_str!("messaging_bindings.rs"),
                "MAX_WASM_AUTHORIZED_TRUSTEES",
            ),
            (
                "legal_bindings.rs",
                include_str!("legal_bindings.rs"),
                "MAX_WASM_LEGAL_AUDIT_ACTIONS",
            ),
            (
                "legal_bindings.rs",
                include_str!("legal_bindings.rs"),
                "MAX_WASM_EDISCOVERY_CORPUS_ITEMS",
            ),
            (
                "legal_bindings.rs",
                include_str!("legal_bindings.rs"),
                "MAX_WASM_RETENTION_RECORDS",
            ),
        ];

        for (path, source, required_name) in required {
            assert!(
                source.contains(required_name),
                "{path} must define and use {required_name}"
            );
        }

        let forbidden = [
            (
                "authority_bindings.rs",
                include_str!("authority_bindings.rs"),
                "let links: Vec<exo_authority::AuthorityLink> = from_json_str(links_json)?;",
            ),
            (
                "authority_bindings.rs",
                include_str!("authority_bindings.rs"),
                "let key_pairs: Vec<(String, String)> = from_json_str(keys_json)?;",
            ),
            (
                "identity_bindings.rs",
                include_str!("identity_bindings.rs"),
                "let shares: Vec<exo_identity::shamir::Share> = from_json_str(shares_json)?;",
            ),
            (
                "escalation_bindings.rs",
                include_str!("escalation_bindings.rs"),
                "let signals: Vec<exo_escalation::detector::DetectionSignal> = from_json_str(signals_json)?;",
            ),
            (
                "escalation_bindings.rs",
                include_str!("escalation_bindings.rs"),
                "from_json_str(entries_json)?;",
            ),
            (
                "escalation_bindings.rs",
                include_str!("escalation_bindings.rs"),
                "let feedbacks: Vec<exo_escalation::feedback::FeedbackEntry> = from_json_str(feedbacks_json)?;",
            ),
            (
                "escalation_bindings.rs",
                include_str!("escalation_bindings.rs"),
                "let mut cases: Vec<exo_escalation::escalation::EscalationCase> = from_json_str(cases_json)?;",
            ),
            (
                "messaging_bindings.rs",
                include_str!("messaging_bindings.rs"),
                "let trustees: Vec<WasmAuthorizedTrustee> = from_json_str(authorized_trustees_json)?;",
            ),
            (
                "legal_bindings.rs",
                include_str!("legal_bindings.rs"),
                "let actions: Vec<exo_legal::fiduciary::AuditEntry> = from_json_str(actions_json)?;",
            ),
            (
                "legal_bindings.rs",
                include_str!("legal_bindings.rs"),
                "let corpus: Vec<exo_legal::evidence::Evidence> = from_json_str(corpus_json)?;",
            ),
            (
                "legal_bindings.rs",
                include_str!("legal_bindings.rs"),
                "let mut records: Vec<exo_legal::records::Record> = from_json_str(records_json)?;",
            ),
        ];

        for (path, source, forbidden_pattern) in forbidden {
            assert!(
                !source.contains(forbidden_pattern),
                "{path} must not deserialize untrusted JSON arrays without explicit count bounds: {forbidden_pattern}"
            );
        }
    }

    #[test]
    fn wasm_decision_forum_vec_inputs_use_explicit_count_bounds() {
        let source = include_str!("decision_forum_bindings.rs");

        for required in [
            "MAX_WASM_FORUM_EMERGENCY_ACTIONS",
            "MAX_WASM_FORUM_CHALLENGES",
        ] {
            assert!(
                source.contains(required),
                "decision forum WASM boundary must define and use {required}"
            );
        }

        for forbidden in [
            "let actions: Vec<decision_forum::emergency::EmergencyAction> = from_json_str(actions_json)?;",
            "from_json_str(challenges_json)?;",
            "let sig_pairs: Vec<(String, String)> = from_json_str(signatures_json)?;",
            "let key_pairs: Vec<(String, String)> = from_json_str(public_keys_json)?;",
        ] {
            assert!(
                !source.contains(forbidden),
                "decision forum WASM boundary must not deserialize untrusted arrays without a count bound: {forbidden}"
            );
        }
    }

    #[test]
    fn wasm_decision_forum_human_vote_exports_fail_closed_without_verified_registry() {
        let source = include_str!("decision_forum_bindings.rs");

        assert!(
            source.contains("decision_forum::human_gate::enforce_human_gate(&policy, &decision)"),
            "legacy WASM human-gate export must use the fail-closed core helper when no trusted human registry is available"
        );
        assert!(
            source.contains("decision_forum::human_gate::is_human_vote(&vote)"),
            "legacy WASM human-vote export must use the fail-closed core helper, not declared actor metadata"
        );
        assert!(
            source.contains("decision_forum::quorum::check_quorum(&registry, &decision)"),
            "legacy WASM quorum export must use the fail-closed core helper when no trusted human registry is available"
        );

        for forbidden in [
            "is_declared_human_vote",
            "check_quorum_with_verified_humans",
            "verified_human_voters",
        ] {
            assert!(
                !source.contains(forbidden),
                "legacy WASM decision-forum exports must not accept or synthesize verified human status via {forbidden}"
            );
        }
    }

    #[test]
    fn wasm_decision_transition_requires_kernel_adjudication() {
        let source = include_str!("decision_forum_bindings.rs");
        let legacy_transition = source
            .split("pub fn wasm_transition_decision(")
            .nth(1)
            .and_then(|section| {
                section
                    .split("pub fn wasm_transition_decision_adjudicated(")
                    .next()
            })
            .expect("legacy decision transition export source");

        assert!(
            legacy_transition.contains("unadjudicated decision transitions are disabled"),
            "legacy WASM decision transition must fail closed without a kernel verdict"
        );
        assert!(
            !legacy_transition.contains(".transition_at("),
            "legacy WASM decision transition must not reach the raw BCTS transition"
        );
        assert!(
            source.contains("Kernel::new") && source.contains(".transition_adjudicated_at("),
            "WASM decision bridge must expose a kernel-adjudicated transition entrypoint"
        );
        assert!(
            source.contains("WasmDecisionTransitionAdjudicatedRequest")
                && source.contains("request_json"),
            "WASM adjudicated decision transition must use a typed bounded request JSON instead of a wide argument list"
        );
        let adjudicated_transition = source
            .split("pub fn wasm_transition_decision_adjudicated(")
            .nth(1)
            .and_then(|section| section.split("/// Add a vote").next())
            .expect("adjudicated decision transition export source");
        assert!(
            adjudicated_transition.contains("InvariantSet::all()"),
            "WASM adjudicated decision transition must enforce the canonical complete invariant set"
        );
        assert!(
            !adjudicated_transition.contains("Kernel::new(constitution, invariant_set)"),
            "WASM adjudicated decision transition must not build a kernel from caller-supplied invariants"
        );
        assert!(
            source
                .split("struct WasmDecisionTransitionAdjudicatedRequest")
                .nth(1)
                .and_then(|section| section.split("/// Create a new DecisionObject").next())
                .is_some_and(|section| !section.contains("invariant_set:")),
            "WASM adjudicated decision transition request must not deserialize caller-supplied invariants"
        );
        assert!(
            !source.contains(
                "timestamp_logical: u32,\n    constitution: &[u8],\n    invariant_set_json: &str,"
            ),
            "WASM adjudicated decision transition must not expose a clippy-wide argument list"
        );
    }

    #[test]
    fn wasm_messaging_bridge_requires_caller_supplied_envelope_metadata() {
        let source = include_str!("messaging_bindings.rs");
        assert!(
            source.contains("message_id") && source.contains("created_physical_ms"),
            "messaging WASM encryption must expose caller-supplied envelope metadata"
        );
        assert!(
            source.contains("ComposeMetadata::new"),
            "messaging WASM encryption must validate caller-supplied envelope metadata"
        );
        assert!(
            source.contains("DeathVerificationCreationMetadata::new")
                && source.contains("DeathConfirmationMetadata::new"),
            "messaging WASM death verification must validate caller-supplied state metadata"
        );
        let initial_payload = source
            .split("pub fn wasm_death_verification_initial_signing_payload")
            .nth(1)
            .and_then(|section| {
                section
                    .split("/// Create a new death verification request")
                    .next()
            })
            .expect("initial death-verification signing payload section");
        assert!(
            initial_payload.contains("created_physical_ms")
                && initial_payload.contains("&metadata.created_at"),
            "initial death-verification signatures must bind the submitted creation timestamp"
        );
        let confirmation_payload = source
            .split("pub fn wasm_death_verification_confirmation_signing_payload")
            .nth(1)
            .and_then(|section| section.split("/// Add a trustee confirmation").next())
            .expect("confirmation death-verification signing payload section");
        assert!(
            confirmation_payload.contains("confirmed_physical_ms")
                && confirmation_payload.contains("&metadata.confirmed_at"),
            "death-verification confirmation signatures must bind the submitted confirmation timestamp"
        );

        let forbidden = [
            format!("{}{}", "Timestamp::", "now_utc()"),
            format!("{}{}", "Uuid::", "new_v4()"),
            "HybridClock::new()".to_string(),
        ];

        for pattern in forbidden {
            assert!(
                !source.contains(&pattern),
                "messaging WASM bindings must receive caller-supplied IDs and HLC timestamps"
            );
        }
    }

    #[test]
    fn wasm_messaging_bridge_does_not_export_x25519_secret_material() {
        let source = include_str!("messaging_bindings.rs");
        let forbidden = [
            ["secret", "_key_hex"].concat(),
            [".secret", ".to_hex()"].concat(),
            [".secret", ".0"].concat(),
        ];
        for pattern in forbidden {
            assert!(
                !source.contains(&pattern),
                "messaging WASM bindings must not export or tuple-access X25519 secret material via {pattern}"
            );
        }
    }

    #[test]
    fn wasm_messaging_bridge_does_not_generate_x25519_keypairs() {
        let source = include_str!("messaging_bindings.rs");
        let production = source
            .split("// ===========================================================================")
            .next()
            .unwrap_or(source);

        assert!(
            !production.contains("X25519KeyPair::generate"),
            "messaging WASM must not generate X25519 key material inside the bridge"
        );
        assert!(
            production.contains("ephemeral_x25519_secret_hex"),
            "messaging WASM encryption must receive caller-supplied ephemeral X25519 material"
        );
    }

    #[test]
    fn wasm_messaging_bridge_does_not_decode_ed25519_signing_secrets() {
        let source = include_str!("messaging_bindings.rs");
        let production = source
            .split("// ===========================================================================")
            .next()
            .unwrap_or(source);

        assert!(
            production.contains("wasm_prepare_encrypted_message"),
            "messaging WASM must expose unsigned encrypted envelopes plus signing bytes"
        );
        assert!(
            production.contains("wasm_attach_message_signature"),
            "messaging WASM must attach caller-produced signatures without importing sender secrets"
        );

        for pattern in [
            "parse_ed25519_signing_seed_hex",
            "sender_signing_key_hex",
            "SecretKey::from_bytes",
            "lock_and_send(",
        ] {
            assert!(
                !production.contains(pattern),
                "messaging WASM bindings must not decode or use Ed25519 signing secrets via {pattern}"
            );
        }
    }

    #[test]
    fn wasm_messaging_legacy_raw_secret_entrypoints_fail_closed() {
        let source = include_str!("messaging_bindings.rs");
        let production = source
            .split("// ===========================================================================")
            .next()
            .unwrap_or(source);

        assert!(
            production
                .contains("raw X25519 secret public derivation is disabled at the WASM boundary"),
            "legacy X25519 raw-secret public derivation must fail closed"
        );
        assert!(
            production.contains("raw Ed25519 sender signing is disabled at the WASM boundary"),
            "legacy raw-secret message encryption must fail closed"
        );
    }

    #[test]
    fn wasm_identity_risk_bridge_requires_caller_supplied_signer_and_time() {
        let source = include_str!("identity_bindings.rs");
        assert!(
            source.contains("attester_secret_hex")
                && source.contains("now_physical_ms")
                && source.contains("now_logical"),
            "risk assessment must expose caller-supplied signer and HLC metadata"
        );

        let forbidden = [
            "HybridClock::new()".to_string(),
            "generate_keypair()".to_string(),
            ["Timestamp::", "now_utc()"].concat(),
        ];

        for pattern in &forbidden {
            assert!(
                !source.contains(pattern),
                "identity WASM risk binding must not fabricate signer or time with {pattern}"
            );
        }
    }

    #[test]
    fn wasm_shamir_split_exposes_caller_supplied_entropy_boundary() {
        let source = include_str!("identity_bindings.rs");
        let production = source
            .split("// ===========================================================================")
            .next()
            .unwrap_or(source);

        assert!(
            production.contains("wasm_shamir_split_with_entropy"),
            "WASM Shamir splitting must expose a caller-supplied entropy entrypoint"
        );
        assert!(
            production.contains("exo_identity::shamir::split_with_entropy"),
            "WASM Shamir splitting must call the entropy-explicit core split API"
        );
        assert!(
            production.contains("entropy"),
            "WASM Shamir split boundary must keep entropy explicit in the public API"
        );
    }

    #[test]
    fn wasm_identity_secret_metadata_has_no_debug_surface() {
        let source = include_str!("identity_bindings.rs");
        let production = source
            .split("// ===========================================================================")
            .next()
            .unwrap_or(source);
        let metadata_def = production
            .split("struct RiskAssessmentMetadata")
            .next()
            .expect("risk metadata definition must exist");

        assert!(
            production.contains("attester_secret_hex"),
            "risk assessment metadata must keep the caller-supplied attester secret explicit"
        );
        assert!(
            !metadata_def.contains("Debug"),
            "secret-bearing risk metadata must not derive or expose Debug"
        );
    }

    #[test]
    fn wasm_core_event_bridge_requires_caller_supplied_metadata() {
        let source = include_str!("core_bindings.rs");
        assert!(
            source.contains("event_id")
                && source.contains("timestamp_physical_ms")
                && source.contains("timestamp_logical"),
            "signed event creation must expose caller-supplied event ID and HLC metadata"
        );

        let forbidden = [
            "CorrelationId::new()".to_string(),
            "HybridClock::new()".to_string(),
            ["Timestamp::", "now_utc()"].concat(),
        ];

        for pattern in &forbidden {
            assert!(
                !source.contains(pattern),
                "core WASM event bindings must not fabricate event metadata with {pattern}"
            );
        }
    }

    #[test]
    fn wasm_core_bridge_does_not_decode_raw_secret_keys() {
        let source = include_str!("core_bindings.rs");
        let production = source
            .split("// ===========================================================================")
            .next()
            .unwrap_or(source);

        assert!(
            production.contains("wasm_event_signing_payload"),
            "core WASM events must expose canonical signing bytes for external signers"
        );
        assert!(
            production.contains("wasm_create_event_with_signature"),
            "core WASM events must accept caller-produced signatures without importing secrets"
        );

        for pattern in [
            "parse_ed25519_secret_array_hex",
            "parse_ed25519_signing_seed_hex",
            "SecretKey::from_bytes",
            "KeyPair::from_secret_bytes",
            "exo_core::events::create_signed_event",
        ] {
            assert!(
                !production.contains(pattern),
                "core WASM bindings must not decode or use raw secret keys via {pattern}"
            );
        }
    }

    #[test]
    fn wasm_core_legacy_raw_secret_entrypoints_fail_closed() {
        let source = include_str!("core_bindings.rs");
        let production = source
            .split("// ===========================================================================")
            .next()
            .unwrap_or(source);

        assert!(
            production.contains("raw secret-key signing is disabled at the WASM boundary"),
            "legacy raw-secret signing entrypoint must fail closed"
        );
        assert!(
            production
                .contains("raw secret-key public derivation is disabled at the WASM boundary"),
            "legacy raw-secret public-key derivation entrypoint must fail closed"
        );
        assert!(
            production.contains("raw secret-key event signing is disabled at the WASM boundary"),
            "legacy raw-secret event creation entrypoint must fail closed"
        );
    }

    #[test]
    fn wasm_avc_bindings_externalize_subject_signing() {
        let source = include_str!("avc_bindings.rs");
        let production = source.split("\n#[cfg(test)]").next().unwrap_or(source);

        assert!(
            production.contains("from_json_str::<AutonomousVolitionCredential>"),
            "AVC WASM credential inputs must use the bounded JSON bridge"
        );
        assert!(
            production.contains("from_json_str::<AvcActionRequest>"),
            "AVC WASM action inputs must use the bounded JSON bridge"
        );
        assert!(
            production.contains("from_json_str::<Signature>"),
            "AVC WASM signature inputs must use the bounded JSON bridge"
        );
        for pattern in [
            "serde_json::from_str(credential_json)",
            "serde_json::from_str(action_json)",
            "serde_json::from_str(subject_signature_json)",
            "hex::decode(value)",
        ] {
            assert!(
                !production.contains(pattern),
                "AVC WASM production bindings must not bypass input bounds via {pattern}"
            );
        }

        assert!(
            production.contains("wasm_avc_action_signing_payload"),
            "AVC WASM bindings must expose canonical action bytes for external signers"
        );
        assert!(
            production.contains("wasm_avc_build_emit_request_from_signature"),
            "AVC WASM bindings must accept externally produced signatures"
        );
        assert!(
            production.contains("raw AVC subject-key signing is disabled at the WASM boundary"),
            "legacy AVC raw subject-key signing must fail closed"
        );
        assert!(
            production.contains(
                "raw AVC subject-key emit request building is disabled at the WASM boundary"
            ),
            "legacy AVC raw subject-key request building must fail closed"
        );

        for pattern in [
            "subject_secret_hex",
            "raw_subject_key_material",
            "SecretKey::from_bytes",
            "KeyPair::from_secret_bytes",
            "crypto::sign(",
        ] {
            assert!(
                !production.contains(pattern),
                "AVC WASM production bindings must not decode, derive, or use raw subject keys via {pattern}"
            );
        }
    }

    #[test]
    fn wasm_core_merkle_bindings_bound_untrusted_arrays() {
        let source = include_str!("core_bindings.rs");
        assert!(
            source.contains("MAX_WASM_MERKLE_LEAVES"),
            "WASM Merkle root/proof bindings must cap caller-supplied leaf arrays"
        );
        assert!(
            source.contains("MAX_WASM_MERKLE_PROOF_HASHES"),
            "WASM Merkle verification must cap caller-supplied proof arrays"
        );
    }

    #[test]
    fn wasm_secret_key_decoding_zeroizes_rust_owned_buffers() {
        let sources = [
            ("identity_bindings.rs", include_str!("identity_bindings.rs")),
            (
                "messaging_bindings.rs",
                include_str!("messaging_bindings.rs"),
            ),
        ];

        for (path, source) in sources {
            assert!(
                source.contains("Zeroizing"),
                "{path} must wrap decoded secret-key buffers in zeroize::Zeroizing"
            );
        }

        let messaging_source = include_str!("messaging_bindings.rs");
        let x25519_helper = messaging_source
            .split("fn parse_x25519_keypair_hex")
            .nth(1)
            .and_then(|rest| {
                rest.split("/// Attach a caller-produced Ed25519 signature")
                    .next()
            })
            .expect("messaging X25519 secret parser must be present");
        assert!(
            x25519_helper.contains("Zeroizing::new("),
            "messaging X25519 secret parser must zeroize Rust-owned decoded buffers"
        );
    }

    #[test]
    fn wasm_gatekeeper_boundary_redacts_internal_errors_and_state() {
        let source = include_str!("gatekeeper_bindings.rs");
        assert!(
            source.contains("gatekeeper_boundary_error"),
            "gatekeeper WASM bindings must centralize sanitized boundary errors"
        );
        assert!(
            source.contains("holon_state_label"),
            "gatekeeper WASM bindings must expose explicit lifecycle labels"
        );

        let forbidden = [
            "format!(\"Reduction error: {e}\")",
            "format!(\"Step error: {e}\")",
            "format!(\"{:?}\", holon.state)",
        ];

        for pattern in forbidden {
            assert!(
                !source.contains(pattern),
                "gatekeeper WASM boundary must not expose internal debug/error details via {pattern}"
            );
        }
    }

    #[test]
    fn wasm_escalation_kanban_validator_uses_bounded_json_bridge() {
        let source = include_str!("escalation_bindings.rs");
        assert!(
            source.contains("from_json_str(column_json)"),
            "Kanban column validation must use the bounded JSON bridge"
        );
        assert!(
            !source.contains("serde_json::from_str(column_json)"),
            "Kanban column validation must not bypass the bounded JSON bridge"
        );
        assert!(
            !source.contains("\"error\": e.to_string()"),
            "Kanban column validation must not return raw serde errors to WASM callers"
        );
    }
}