xoonya 1.5.0 → 1.7.0

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/README.md CHANGED
@@ -20,6 +20,20 @@ The package and service now also expose a default proof center surface so the in
20
20
  - compact compatibility helpers preserved, but not treated as the public conceptual center
21
21
  - proof remains settlement-agnostic and workflow-agnostic by default
22
22
 
23
+ The current `1.6.0` companion slice adds guided default proof loops on top of that proof center:
24
+
25
+ - default proof-loop catalog and detail routes
26
+ - a direct proof-loop run route for the canonical proof-native path
27
+ - package helpers that build, verify, and summarize the proof bundle boundary in one step
28
+
29
+ This keeps `xoonya` narrow while making the intended proof path easier to inspect alongside the newer guided transaction-loop work in `xytara`.
30
+
31
+ The current `1.7.0` companion slice adds a lightweight transaction proof-handoff surface:
32
+
33
+ - a transaction proof-handoff center that states the expected handoff contract
34
+ - a direct handoff run path that turns transaction-side proof handoff data into a proof-native bundle
35
+ - summary helpers that make the recommended proof follow-through explicit without widening proof scope
36
+
23
37
  ## Proof Model
24
38
 
25
39
  The primary proof model is layered:
@@ -72,6 +86,7 @@ The package quickstart demonstrates:
72
86
  - canonical receipt emission
73
87
  - proof bundle construction
74
88
  - proof bundle verification
89
+ - transaction proof-handoff follow-through
75
90
 
76
91
  ## Service Quickstart
77
92
 
@@ -98,6 +113,15 @@ Environment example:
98
113
  Primary proof-model routes:
99
114
 
100
115
  - `GET /health`
116
+ - `GET /v1/proof-center`
117
+ - `GET /v1/proof-center/summary`
118
+ - `GET /v1/proof-center/default-loops`
119
+ - `GET /v1/proof-center/default-loops/summary`
120
+ - `GET /v1/proof-center/default-loop`
121
+ - `POST /v1/proof-center/default-loops/run`
122
+ - `GET /v1/proof-center/transaction-handoff`
123
+ - `GET /v1/proof-center/transaction-handoff/summary`
124
+ - `POST /v1/proof-center/transaction-handoff/run`
101
125
  - `POST /v1/events/canonicalize`
102
126
  - `POST /v1/events/hash`
103
127
  - `POST /v1/receipts/emit`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xoonya",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Deterministic proof SDK for canonical events, receipts, proof bundles, and verification.",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
package/proof_sdk.js CHANGED
@@ -793,9 +793,273 @@ function summarizeDefaultProofCenter() {
793
793
  };
794
794
  }
795
795
 
796
+ function buildDefaultProofLoops() {
797
+ const center = buildDefaultProofCenter();
798
+ return [
799
+ {
800
+ loop_ref: "default.proof.bundle.loop",
801
+ title: "Default Proof Bundle Loop",
802
+ description: "Canonicalize an event, emit a receipt, build a proof bundle, and verify it in one proof-native path.",
803
+ stage_count: 5,
804
+ stages: ["canonicalize", "hash", "emit_receipt", "build_bundle", "verify_bundle"],
805
+ event_version: center.proof_model.event_version,
806
+ receipt_version: center.proof_model.receipt_version,
807
+ proof_bundle_version: center.proof_model.proof_bundle_version
808
+ },
809
+ {
810
+ loop_ref: "default.proof.receipt.loop",
811
+ title: "Default Proof Receipt Loop",
812
+ description: "Canonicalize an event, emit a receipt, and verify the resulting proof-native receipt path.",
813
+ stage_count: 4,
814
+ stages: ["canonicalize", "hash", "emit_receipt", "verify_receipt"],
815
+ event_version: center.proof_model.event_version,
816
+ receipt_version: center.proof_model.receipt_version,
817
+ proof_bundle_version: center.proof_model.proof_bundle_version
818
+ }
819
+ ];
820
+ }
821
+
822
+ function buildDefaultProofLoopCatalog() {
823
+ const center = buildDefaultProofCenter();
824
+ const loops = buildDefaultProofLoops();
825
+ return {
826
+ ok: true,
827
+ brand: center.brand,
828
+ product: "default proof loops",
829
+ event_version: center.proof_model.event_version,
830
+ receipt_version: center.proof_model.receipt_version,
831
+ proof_bundle_version: center.proof_model.proof_bundle_version,
832
+ loop_count: loops.length,
833
+ loops
834
+ };
835
+ }
836
+
837
+ function summarizeDefaultProofLoops() {
838
+ const catalog = buildDefaultProofLoopCatalog();
839
+ return {
840
+ ok: true,
841
+ brand: catalog.brand,
842
+ product: catalog.product,
843
+ event_version: catalog.event_version,
844
+ receipt_version: catalog.receipt_version,
845
+ proof_bundle_version: catalog.proof_bundle_version,
846
+ loop_count: catalog.loop_count,
847
+ loop_refs: catalog.loops.map((entry) => entry && entry.loop_ref).filter(Boolean),
848
+ loops: catalog.loops
849
+ };
850
+ }
851
+
852
+ function getDefaultProofLoop(ref) {
853
+ const normalized = String(ref || "").trim();
854
+ if (!normalized) return null;
855
+ return buildDefaultProofLoops().find((entry) => entry && entry.loop_ref === normalized) || null;
856
+ }
857
+
858
+ function summarizeGuidedProofLoopBundle(bundle) {
859
+ const item = bundle || {};
860
+ const loop = item.loop || {};
861
+ const canonicalEvent = item.canonical_event || {};
862
+ const receipt = item.receipt || {};
863
+ const proofBundle = item.proof_bundle || {};
864
+ const verification = item.bundle_verification || {};
865
+ const receiptVerification = item.receipt_verification || {};
866
+ return {
867
+ ok: item.ok === true,
868
+ loop_ref: loop.loop_ref || null,
869
+ stage_count: Number(loop.stage_count || 0),
870
+ event_version: canonicalEvent.version || null,
871
+ receipt_version: receipt.version || null,
872
+ proof_bundle_version: proofBundle.version || null,
873
+ receipt_id: receipt.receipt_id || null,
874
+ event_commitment: receipt.event_commitment || null,
875
+ bundle_valid: verification.valid === true,
876
+ receipt_valid: receiptVerification.valid === true
877
+ };
878
+ }
879
+
880
+ function buildTransactionProofHandoffCenter() {
881
+ return {
882
+ ok: true,
883
+ brand: "xoonya",
884
+ product: "transaction proof handoff center",
885
+ supported_source_refs: [
886
+ "transaction.registry.anchor",
887
+ "transaction.runtime.consequence",
888
+ "transaction.trust.receipt",
889
+ "transaction.admission.settlement",
890
+ "transaction.job.commitment"
891
+ ],
892
+ recommended_loop: "default.proof.bundle.loop",
893
+ required_fields: ["transaction_id", "receipt_id", "proof_ref"],
894
+ optional_fields: ["source_ref", "task_refs", "delivery_count", "payment_ledger_count"]
895
+ };
896
+ }
897
+
898
+ function summarizeTransactionProofHandoffCenter() {
899
+ const center = buildTransactionProofHandoffCenter();
900
+ return {
901
+ ok: true,
902
+ brand: center.brand,
903
+ product: center.product,
904
+ source_ref_count: Array.isArray(center.supported_source_refs) ? center.supported_source_refs.length : 0,
905
+ supported_source_refs: center.supported_source_refs,
906
+ required_field_count: Array.isArray(center.required_fields) ? center.required_fields.length : 0,
907
+ required_fields: center.required_fields,
908
+ recommended_loop: center.recommended_loop
909
+ };
910
+ }
911
+
912
+ function canonicalizeTransactionProofHandoff(input) {
913
+ const body = input && typeof input === "object" && !Array.isArray(input) ? input : {};
914
+ const handoff = body.handoff_bundle && typeof body.handoff_bundle === "object" && !Array.isArray(body.handoff_bundle)
915
+ ? body.handoff_bundle
916
+ : body;
917
+ const transactionId = normalizeString(handoff.transaction_id);
918
+ const receiptId = normalizeString(handoff.receipt_id);
919
+ const proofRef = normalizeString(handoff.proof_ref);
920
+ if (!transactionId) throw new Error("handoff_bundle.transaction_id required");
921
+ if (!receiptId) throw new Error("handoff_bundle.receipt_id required");
922
+ if (!proofRef) throw new Error("handoff_bundle.proof_ref required");
923
+ const sourceRef = normalizeString(handoff.source_ref || body.source_ref || "transaction.proof_handoff");
924
+ const taskRefs = normalizeStringArray(handoff.task_refs, "handoff_bundle.task_refs") || [];
925
+ const deliveryCount = Number.isFinite(Number(handoff.delivery_count)) ? Number(handoff.delivery_count) : 0;
926
+ const paymentLedgerCount = Number.isFinite(Number(handoff.payment_ledger_count)) ? Number(handoff.payment_ledger_count) : 0;
927
+ return {
928
+ source_ref: sourceRef,
929
+ transaction_id: transactionId,
930
+ receipt_id: receiptId,
931
+ proof_ref: proofRef,
932
+ task_refs: taskRefs,
933
+ delivery_count: deliveryCount,
934
+ payment_ledger_count: paymentLedgerCount
935
+ };
936
+ }
937
+
938
+ function buildTransactionProofHandoffEvent(input) {
939
+ const handoff = canonicalizeTransactionProofHandoff(input);
940
+ const eventHash = canonicalJsonSha256Hex({
941
+ source_ref: handoff.source_ref,
942
+ transaction_id: handoff.transaction_id,
943
+ receipt_id: handoff.receipt_id,
944
+ proof_ref: handoff.proof_ref,
945
+ task_refs: handoff.task_refs,
946
+ delivery_count: handoff.delivery_count,
947
+ payment_ledger_count: handoff.payment_ledger_count
948
+ });
949
+ return canonicalizeEvent({
950
+ version: CANONICAL_EVENT_BUNDLE_VERSION,
951
+ domain_id: "xytara.transaction_handoff",
952
+ sequence: handoff.transaction_id,
953
+ actor_id: "xytara.transaction_center",
954
+ adapter_source: "xytara.proof_handoff",
955
+ event_type: `${handoff.source_ref}.proof_handoff`,
956
+ event_hash: eventHash,
957
+ semantic_slots: handoff
958
+ });
959
+ }
960
+
961
+ function runTransactionProofHandoff(input) {
962
+ const handoff = canonicalizeTransactionProofHandoff(input);
963
+ const event = buildTransactionProofHandoffEvent(input);
964
+ const options = input && input.options && typeof input.options === "object" && !Array.isArray(input.options)
965
+ ? input.options
966
+ : {};
967
+ const receipt = emitReceipt(event, options);
968
+ const proofBundle = buildProofBundle({
969
+ ceb: event,
970
+ receipt,
971
+ evidence_refs: Array.isArray(input && input.evidence_refs) ? input.evidence_refs : [handoff.proof_ref],
972
+ metadata: {
973
+ handoff_kind: "transaction_proof_handoff",
974
+ source_ref: handoff.source_ref
975
+ }
976
+ });
977
+ return {
978
+ ok: true,
979
+ handoff,
980
+ canonical_event: event,
981
+ event_commitment: hashEvent(event),
982
+ receipt,
983
+ proof_bundle: proofBundle,
984
+ bundle_verification: verifyProofBundle(proofBundle),
985
+ recommended_loop: "default.proof.bundle.loop"
986
+ };
987
+ }
988
+
989
+ function summarizeTransactionProofHandoff(bundle) {
990
+ const item = bundle && typeof bundle === "object" ? bundle : {};
991
+ const handoff = item.handoff && typeof item.handoff === "object" ? item.handoff : {};
992
+ const receipt = item.receipt && typeof item.receipt === "object" ? item.receipt : {};
993
+ const proofBundle = item.proof_bundle && typeof item.proof_bundle === "object" ? item.proof_bundle : {};
994
+ const verification = item.bundle_verification && typeof item.bundle_verification === "object" ? item.bundle_verification : {};
995
+ return {
996
+ ok: item.ok === true,
997
+ source_ref: handoff.source_ref || null,
998
+ transaction_id: handoff.transaction_id || null,
999
+ receipt_id: handoff.receipt_id || null,
1000
+ proof_ref: handoff.proof_ref || null,
1001
+ task_ref_count: Array.isArray(handoff.task_refs) ? handoff.task_refs.length : 0,
1002
+ proof_bundle_version: proofBundle.version || null,
1003
+ emitted_receipt_id: receipt.receipt_id || null,
1004
+ bundle_valid: verification.valid === true,
1005
+ recommended_loop: item.recommended_loop || null
1006
+ };
1007
+ }
1008
+
1009
+ function runDefaultProofLoop(loopRef, input) {
1010
+ const loop = getDefaultProofLoop(loopRef);
1011
+ if (!loop) {
1012
+ throw new Error("default_proof_loop_not_found");
1013
+ }
1014
+ const body = input && typeof input === "object" && !Array.isArray(input) ? input : {};
1015
+ const event = canonicalizeEvent(body.event || body.ceb);
1016
+ const receiptOptions = body.options && typeof body.options === "object" && !Array.isArray(body.options)
1017
+ ? body.options
1018
+ : {};
1019
+ const receipt = emitReceipt(event, receiptOptions);
1020
+ const bundle = buildProofBundle({
1021
+ ceb: event,
1022
+ receipt,
1023
+ anchors: Array.isArray(body.anchors) ? body.anchors : undefined,
1024
+ attestations: Array.isArray(body.attestations) ? body.attestations : undefined,
1025
+ evidence_refs: Array.isArray(body.evidence_refs) ? body.evidence_refs : undefined,
1026
+ batch_refs: Array.isArray(body.batch_refs) ? body.batch_refs : undefined,
1027
+ metadata: body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? body.metadata : undefined
1028
+ });
1029
+ const bundleVerification = verifyProofBundle(bundle);
1030
+ const receiptVerification = receipt.compatibility && receipt.compatibility.legacy_receipt
1031
+ ? verifyReceipt(
1032
+ receipt.compatibility.legacy_receipt.opreturn_hex,
1033
+ Buffer.from(canonicalize(event), "utf8")
1034
+ )
1035
+ : { valid: true, reason: null, version: CANONICAL_RECEIPT_VERSION };
1036
+ return {
1037
+ ok: true,
1038
+ loop,
1039
+ canonical_event: event,
1040
+ event_commitment: hashEvent(event),
1041
+ receipt,
1042
+ receipt_verification: receiptVerification,
1043
+ proof_bundle: bundle,
1044
+ bundle_verification: bundleVerification
1045
+ };
1046
+ }
1047
+
796
1048
  module.exports = {
797
1049
  buildDefaultProofCenter,
1050
+ buildDefaultProofLoopCatalog,
1051
+ buildDefaultProofLoops,
1052
+ buildTransactionProofHandoffCenter,
1053
+ buildTransactionProofHandoffEvent,
1054
+ getDefaultProofLoop,
1055
+ canonicalizeTransactionProofHandoff,
1056
+ runDefaultProofLoop,
1057
+ runTransactionProofHandoff,
798
1058
  summarizeDefaultProofCenter,
1059
+ summarizeDefaultProofLoops,
1060
+ summarizeGuidedProofLoopBundle,
1061
+ summarizeTransactionProofHandoff,
1062
+ summarizeTransactionProofHandoffCenter,
799
1063
  canonicalizeEvent,
800
1064
  hashEvent,
801
1065
  emitReceipt,
package/server.js CHANGED
@@ -87,6 +87,59 @@ async function routeRequest(req, res) {
87
87
  return;
88
88
  }
89
89
 
90
+ if (req.method === 'GET' && url.pathname === '/v1/proof-center/default-loops') {
91
+ sendJson(res, 200, proofSdk.buildDefaultProofLoopCatalog());
92
+ return;
93
+ }
94
+
95
+ if (req.method === 'GET' && url.pathname === '/v1/proof-center/default-loops/summary') {
96
+ sendJson(res, 200, proofSdk.summarizeDefaultProofLoops());
97
+ return;
98
+ }
99
+
100
+ if (req.method === 'GET' && url.pathname === '/v1/proof-center/transaction-handoff') {
101
+ sendJson(res, 200, proofSdk.buildTransactionProofHandoffCenter());
102
+ return;
103
+ }
104
+
105
+ if (req.method === 'GET' && url.pathname === '/v1/proof-center/transaction-handoff/summary') {
106
+ sendJson(res, 200, proofSdk.summarizeTransactionProofHandoffCenter());
107
+ return;
108
+ }
109
+
110
+ if (req.method === 'GET' && url.pathname === '/v1/proof-center/default-loop') {
111
+ const loop = proofSdk.getDefaultProofLoop(url.searchParams.get('ref'));
112
+ if (!loop) {
113
+ sendJson(res, 404, { ok: false, error: 'default_proof_loop_not_found' });
114
+ return;
115
+ }
116
+ sendJson(res, 200, { ok: true, loop });
117
+ return;
118
+ }
119
+
120
+ if (req.method === 'POST' && url.pathname === '/v1/proof-center/default-loops/run') {
121
+ try {
122
+ const body = await readJsonBody(req);
123
+ const loopRef = String(body.loop_ref || '').trim();
124
+ const result = proofSdk.runDefaultProofLoop(loopRef, body);
125
+ sendJson(res, 200, result);
126
+ } catch (error) {
127
+ sendJson(res, error.message === 'default_proof_loop_not_found' ? 404 : 400, { ok: false, reason: error.message });
128
+ }
129
+ return;
130
+ }
131
+
132
+ if (req.method === 'POST' && url.pathname === '/v1/proof-center/transaction-handoff/run') {
133
+ try {
134
+ const body = await readJsonBody(req);
135
+ const result = proofSdk.runTransactionProofHandoff(body);
136
+ sendJson(res, 200, result);
137
+ } catch (error) {
138
+ sendJson(res, 400, { ok: false, reason: error.message });
139
+ }
140
+ return;
141
+ }
142
+
90
143
  if (req.method === 'POST' && url.pathname === '/v1/receipts/verify') {
91
144
  try {
92
145
  const body = await readJsonBody(req);