@derec-alliance/react-native 0.0.5 → 0.0.7

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/cpp/derec_ffi.h CHANGED
@@ -65,13 +65,6 @@
65
65
  */
66
66
  #define DEFAULT_THRESHOLD 3
67
67
 
68
- /**
69
- * Number of recent share versions each helper retains, absent an explicit
70
- * [`DeRecProtocolBuilder::with_keep_versions_count`](crate::protocol::DeRecProtocolBuilder::with_keep_versions_count) call. Sole definition
71
- * of the value; see [`DEFAULT_THRESHOLD`].
72
- */
73
- #define DEFAULT_KEEP_VERSIONS_COUNT 3
74
-
75
68
  /**
76
69
  * Maximum accepted transport URI length, in bytes.
77
70
  *
@@ -296,18 +289,6 @@
296
289
  */
297
290
  #define DEREC_CODE_NO_USABLE_ENDPOINT 121
298
291
 
299
- /**
300
- * Both plaintext opt-in flags were set explicitly and disagree: the
301
- * deprecated `unsafe_http` says one thing and `unsafe_connection` the
302
- * other. Raised at protocol construction rather than resolved by
303
- * precedence, because the flag precedence would favour is the one being
304
- * removed, and a configuration layer that emits every field
305
- * unconditionally would otherwise let a defaulted value silently beat a
306
- * deliberate one. Set only `unsafe_connection`.
307
- * `DEREC_CATEGORY_INVALID_INPUT`.
308
- */
309
- #define DEREC_CODE_CONFLICTING_PLAINTEXT_OPT_IN 122
310
-
311
292
  /**
312
293
  * Discriminants selecting which message [`derec_decode_message_json`] and
313
294
  * [`derec_encode_message_json`] operate on.
@@ -678,6 +659,20 @@ typedef struct ExtractPrePairResponseResult {
678
659
  struct DeRecBuffer response_proto_bytes;
679
660
  } ExtractPrePairResponseResult;
680
661
 
662
+ typedef struct ProducePrePairNoKeysResponseMessageResult {
663
+ struct DeRecError error;
664
+ /**
665
+ * Serialized outer plaintext `DeRecMessage` envelope carrying a
666
+ * `PrePairResponseMessage` with freshly generated public keys.
667
+ */
668
+ struct DeRecBuffer envelope_wire_bytes;
669
+ /**
670
+ * The secret key material generated for this pairing. The caller MUST
671
+ * persist it: the `PairRequestMessage` that follows is encrypted to it.
672
+ */
673
+ struct DeRecBuffer secret_key_material;
674
+ } ProducePrePairNoKeysResponseMessageResult;
675
+
681
676
  /**
682
677
  * On success the two key buffers hold the validated public keys republished
683
678
  * by the contact creator. On failure (status non-Ok, hash mismatch, etc.)
@@ -693,6 +688,15 @@ typedef struct ProcessPrePairResponseMessageResult {
693
688
  uint64_t nonce;
694
689
  } ProcessPrePairResponseMessageResult;
695
690
 
691
+ typedef struct PairingFingerprintResult {
692
+ struct DeRecError error;
693
+ /**
694
+ * NUL-terminated UTF-8 fingerprint. Free with
695
+ * [`crate::interop::ffi::common::derec_free_string`]. Null on failure.
696
+ */
697
+ char *fingerprint;
698
+ } PairingFingerprintResult;
699
+
696
700
  /**
697
701
  * Result type for [`derec_protocol_new`].
698
702
  */
@@ -796,6 +800,12 @@ typedef struct ChannelStoreCallbacks {
796
800
 
797
801
  /**
798
802
  * Caller-supplied callbacks for secret persistence.
803
+ *
804
+ * `load_many` receives the requested channel ids as a JSON array of
805
+ * numbers and returns a JSON array with exactly one entry per requested id,
806
+ * in the same order: the same `{kind, bytes}` record `load` returns, or
807
+ * `null` when nothing of `kind` is stored for that channel. Whether a
808
+ * missing entry is an error is decided by the library, not the callback.
799
809
  */
800
810
  typedef struct SecretStoreCallbacks {
801
811
  void *user_data;
@@ -805,6 +815,13 @@ typedef struct SecretStoreCallbacks {
805
815
  uint32_t kind,
806
816
  uint8_t **out_ptr,
807
817
  size_t *out_len);
818
+ int32_t (*load_many)(void *user_data,
819
+ uint64_t secret_id,
820
+ const uint8_t *channel_ids_json_ptr,
821
+ size_t channel_ids_json_len,
822
+ uint32_t kind,
823
+ uint8_t **out_ptr,
824
+ size_t *out_len);
808
825
  int32_t (*save)(void *user_data,
809
826
  uint64_t secret_id,
810
827
  uint64_t channel_id,
@@ -820,6 +837,11 @@ typedef struct SecretStoreCallbacks {
820
837
  * arrays (`channel_ids[]`, `versions[]`) cross the FFI as JSON
821
838
  * strings, matching the `Vec<u8>` ↔ JSON-array convention used for
822
839
  * every other wire-format buffer in this module.
840
+ *
841
+ * `keep_list` returns the JSON encoding of the application's answer:
842
+ * `null` for no list (the owner sends an empty `keepList` and helpers keep
843
+ * every version) or an array of version numbers. An empty buffer or return code `1` also
844
+ * means `null`.
823
845
  */
824
846
  typedef struct ShareStoreCallbacks {
825
847
  void *user_data;
@@ -854,6 +876,16 @@ typedef struct ShareStoreCallbacks {
854
876
  const uint8_t *share_json_ptr,
855
877
  size_t share_json_len);
856
878
  int32_t (*remove_channel)(void *user_data, uint64_t secret_id, uint64_t channel_id);
879
+ int32_t (*remove_versions)(void *user_data,
880
+ uint64_t secret_id,
881
+ uint64_t channel_id,
882
+ const uint8_t *versions_json_ptr,
883
+ size_t versions_json_len);
884
+ int32_t (*keep_list)(void *user_data,
885
+ uint64_t secret_id,
886
+ uint32_t version,
887
+ uint8_t **out_ptr,
888
+ size_t *out_len);
857
889
  void (*free_buffer)(void *user_data, uint8_t *ptr, size_t len);
858
890
  } ShareStoreCallbacks;
859
891
 
@@ -977,6 +1009,51 @@ typedef struct DeRecProtocolEventsResult {
977
1009
  struct DeRecBuffer events_json;
978
1010
  } DeRecProtocolEventsResult;
979
1011
 
1012
+ /**
1013
+ * Result of [`derec_protocol_process`].
1014
+ *
1015
+ * A failed call still carries the events produced before the failure in
1016
+ * `events_json`, and names the channel the failing message came from.
1017
+ */
1018
+ typedef struct DeRecProtocolProcessResult {
1019
+ struct DeRecError error;
1020
+ /**
1021
+ * UTF-8 JSON array of events, as on [`DeRecProtocolEventsResult`].
1022
+ * Filled on success and on failure.
1023
+ */
1024
+ struct DeRecBuffer events_json;
1025
+ /**
1026
+ * Whether `channel_id` is set. False on success and when the bytes
1027
+ * were not a decodable envelope.
1028
+ */
1029
+ bool has_channel_id;
1030
+ /**
1031
+ * The channel the failing message came from.
1032
+ */
1033
+ uint64_t channel_id;
1034
+ } DeRecProtocolProcessResult;
1035
+
1036
+ /**
1037
+ * Result of [`derec_protocol_restore`].
1038
+ *
1039
+ * A restore refused because channels already exist at the recovered ids
1040
+ * reports which ones in `conflicting_channel_ids_json`, so the application
1041
+ * can clear exactly those and retry.
1042
+ */
1043
+ typedef struct DeRecProtocolRestoreResult {
1044
+ struct DeRecError error;
1045
+ /**
1046
+ * UTF-8 JSON array of events, as on [`DeRecProtocolEventsResult`].
1047
+ */
1048
+ struct DeRecBuffer events_json;
1049
+ /**
1050
+ * UTF-8 JSON array of decimal-string channel ids. Non-empty only when
1051
+ * `error.code` is `DEREC_CODE_RESTORE_CONFLICT`. Caller releases via
1052
+ * [`crate::interop::ffi::derec_free_buffer`].
1053
+ */
1054
+ struct DeRecBuffer conflicting_channel_ids_json;
1055
+ } DeRecProtocolRestoreResult;
1056
+
980
1057
  /**
981
1058
  * Result type for fingerprint accessors.
982
1059
  */
@@ -1372,34 +1449,9 @@ struct CreateContactMessageResult create_contact_message(uint64_t channel_id,
1372
1449
  uint32_t has_nonce,
1373
1450
  uint64_t nonce);
1374
1451
 
1375
- /**
1376
- * Structurally validate a proto-encoded `ContactMessage`. Returns a
1377
- * successful [`DeRecError`] iff the contact's `(contact_mode, inline keys,
1378
- * binding hash)` tuple satisfies the per-mode invariants enforced by the
1379
- * pairing primitives. Intended for bindings to call at their parse
1380
- * boundary (e.g. `FromProtoBytes`) so that the decoded value handed to
1381
- * application code is guaranteed well-formed.
1382
- *
1383
- * Failure codes:
1384
- * - [`DEREC_CODE_FFI_BAD_PROTO`] if the bytes do not decode as a
1385
- * `ContactMessage`.
1386
- * - The library's `InvalidContactMessage` error code on any structural
1387
- * violation (unknown `contact_mode`, mode/field mismatch, wrong
1388
- * binding-hash length).
1389
- *
1390
- * # Safety
1391
- *
1392
- * `contact_message_ptr` must point to a readable range of
1393
- * `contact_message_len` bytes (or be null with `len == 0`).
1394
- */
1395
- struct DeRecError validate_contact_message(const uint8_t *contact_message_ptr,
1396
- size_t contact_message_len);
1397
-
1398
1452
  /**
1399
1453
  * Encodes a JSON [`ContactMessageDto`] to `ContactMessage` proto wire bytes.
1400
- * Structurally validates the input first so a locally-constructed contact
1401
- * that violates the mode/field invariant is rejected at the boundary rather
1402
- * than silently serialized.
1454
+ * See [`crate::primitives::pairing::request::encode_contact`].
1403
1455
  *
1404
1456
  * The JSON shape is [`ContactMessageDto`]'s serde representation with one
1405
1457
  * adjustment applied at this seam: `channel_id` and `nonce` are decimal
@@ -1419,9 +1471,7 @@ struct EncodeContactMessageResult encode_contact_message(const uint8_t *contact_
1419
1471
  /**
1420
1472
  * Decodes proto-encoded `ContactMessage` wire bytes into the JSON
1421
1473
  * [`ContactMessageDto`] shape described on [`encode_contact_message`].
1422
- * Structurally validates the decoded value before returning it to
1423
- * application code so consumers can trust the mode/field invariants
1424
- * documented on the wire format.
1474
+ * See [`crate::primitives::pairing::request::decode_contact`].
1425
1475
  *
1426
1476
  * # Safety
1427
1477
  *
@@ -1452,6 +1502,10 @@ struct ProducePairRequestMessageResult produce_pair_request_message(int32_t send
1452
1502
  size_t parameter_range_len);
1453
1503
 
1454
1504
  /**
1505
+ * `parameter_range_ptr` may be null / zero-length to declare no parameter
1506
+ * range; otherwise it must be serialized [`derec_proto::ParameterRange`]
1507
+ * proto bytes. See [`crate::primitives::pairing::request::extract`].
1508
+ *
1455
1509
  * # Safety
1456
1510
  *
1457
1511
  * Non-null input pointers must point to the corresponding readable byte ranges.
@@ -1459,7 +1513,9 @@ struct ProducePairRequestMessageResult produce_pair_request_message(int32_t send
1459
1513
  struct ExtractPairRequestResult extract_pair_request(const uint8_t *request_ptr,
1460
1514
  size_t request_len,
1461
1515
  const uint8_t *secret_key_material_ptr,
1462
- size_t secret_key_material_len);
1516
+ size_t secret_key_material_len,
1517
+ const uint8_t *parameter_range_ptr,
1518
+ size_t parameter_range_len);
1463
1519
 
1464
1520
  /**
1465
1521
  * `request_proto_ptr` / `request_proto_len` must be the `request_proto_bytes`
@@ -1499,6 +1555,10 @@ struct ExtractPairResponseResult extract_pair_response(const uint8_t *response_p
1499
1555
  * `response_proto_ptr` / `response_proto_len` must be the
1500
1556
  * `response_proto_bytes` returned by [`extract_pair_response`].
1501
1557
  *
1558
+ * `parameter_range_ptr` may be null / zero-length to declare no parameter
1559
+ * range; otherwise it must be serialized [`derec_proto::ParameterRange`]
1560
+ * proto bytes. See [`crate::primitives::pairing::response::process`].
1561
+ *
1502
1562
  * # Safety
1503
1563
  *
1504
1564
  * Non-null input pointers must point to the corresponding readable byte ranges.
@@ -1508,7 +1568,9 @@ struct ProcessPairResponseMessageResult process_pair_response_message(const uint
1508
1568
  const uint8_t *response_proto_ptr,
1509
1569
  size_t response_proto_len,
1510
1570
  const uint8_t *secret_key_material_ptr,
1511
- size_t secret_key_material_len);
1571
+ size_t secret_key_material_len,
1572
+ const uint8_t *parameter_range_ptr,
1573
+ size_t parameter_range_len);
1512
1574
 
1513
1575
  /**
1514
1576
  * Builds a plaintext `PrePairRequestMessage` envelope. Used by the scanner
@@ -1563,6 +1625,55 @@ struct ProducePrePairResponseMessageResult produce_pre_pair_response_message(uin
1563
1625
  struct ExtractPrePairResponseResult extract_pre_pair_response(const uint8_t *envelope_ptr,
1564
1626
  size_t envelope_len);
1565
1627
 
1628
+ /**
1629
+ * Contact-creator side of a `NO_KEYS` pairing: generates key material and
1630
+ * answers the `PrePairRequestMessage` with its public half.
1631
+ *
1632
+ * The caller MUST first match the request's `nonce` against the contact it
1633
+ * issued — the only thing that authenticates a `NO_KEYS` request — and MUST
1634
+ * keep the resulting channel unusable until both sides confirm
1635
+ * [`pairing_fingerprint`] out of band.
1636
+ *
1637
+ * `request_proto_ptr` / `request_proto_len` must be the `request_proto_bytes`
1638
+ * returned by [`extract_pre_pair_request`].
1639
+ *
1640
+ * # Safety
1641
+ *
1642
+ * Non-null input pointers must point to the corresponding readable byte ranges.
1643
+ */
1644
+ struct ProducePrePairNoKeysResponseMessageResult produce_pre_pair_no_keys_response_message(uint64_t channel_id,
1645
+ const uint8_t *request_proto_ptr,
1646
+ size_t request_proto_len);
1647
+
1648
+ /**
1649
+ * Scanner side of a `NO_KEYS` pairing: accepts the contact creator's public
1650
+ * keys. There is no binding hash to check them against, so the channel this
1651
+ * leads to MUST stay unusable until both sides confirm
1652
+ * [`pairing_fingerprint`] out of band.
1653
+ *
1654
+ * `response_proto_ptr` / `response_proto_len` must be the
1655
+ * `response_proto_bytes` returned by [`extract_pre_pair_response`].
1656
+ *
1657
+ * # Safety
1658
+ *
1659
+ * Non-null input pointers must point to the corresponding readable byte ranges.
1660
+ */
1661
+ struct ProcessPrePairResponseMessageResult process_pre_pair_no_keys_response_message(const uint8_t *contact_message_ptr,
1662
+ size_t contact_message_len,
1663
+ const uint8_t *response_proto_ptr,
1664
+ size_t response_proto_len);
1665
+
1666
+ /**
1667
+ * The human-readable fingerprint of a pairing's shared key. See
1668
+ * [`crate::primitives::pairing::fingerprint`].
1669
+ *
1670
+ * # Safety
1671
+ *
1672
+ * `shared_key_ptr` must point to `shared_key_len` readable bytes.
1673
+ */
1674
+ struct PairingFingerprintResult pairing_fingerprint(const uint8_t *shared_key_ptr,
1675
+ size_t shared_key_len);
1676
+
1566
1677
  /**
1567
1678
  * Scanner-side: validates a decoded `PrePairResponseMessage` against the
1568
1679
  * original `ContactMessage`'s SHA-384 binding hash. On success returns the
@@ -1588,30 +1699,24 @@ struct ProcessPrePairResponseMessageResult process_pre_pair_response_message(con
1588
1699
  * arguments in a single call — e.g. Go via `purego` (no cgo), which
1589
1700
  * panics with "too many stack arguments" past a handful of
1590
1701
  * parameters. Scalar configuration is bundled into a single JSON
1591
- * buffer; `communication_info` stays a separate proto-encoded
1592
- * `CommunicationInfo` buffer; the 6 store/transport callback structs
1702
+ * buffer; `communication_info` travels either in that JSON or as a
1703
+ * separate proto-encoded `CommunicationInfo` buffer; the 6 store/transport callback structs
1593
1704
  * are still passed as individual pointers, since purego marshals
1594
1705
  * pointer-sized arguments natively.
1595
1706
  *
1596
- * `config_json` must deserialize to the following shape — all field
1597
- * names `snake_case`. `secret_id`, `own_transport_uri` and
1598
- * `own_transport_protocol` are the only genuinely required fields;
1599
- * `threshold`, `keep_versions_count`, `auto_respond_on_failure`,
1600
- * `unpair_ack`, `auto_reply_to` and `auto_accept` may each be omitted, in
1601
- * which case the value matches
1707
+ * `config_json` must deserialize to [`ProtocolConfig`] — all field names
1708
+ * `snake_case`. `secret_id` is the only required field. Every other field
1709
+ * may be omitted, in which case the value matches
1602
1710
  * [`crate::protocol::DeRecProtocolBuilder::new`]'s own default for that
1603
- * setting — see [`ProtocolConfig`]'s field-level `#[serde(default)]`
1604
- * attributes, which read the same constants the builder does:
1711
+ * setting: [`ProtocolConfig`]'s field-level `#[serde(default)]` attributes
1712
+ * read the same constants the builder does. A field that is present is
1713
+ * used as given, including `0`, and validated by the builder.
1605
1714
  *
1606
1715
  * ```json
1607
1716
  * {
1608
1717
  * "secret_id": "12345678901234567890",
1609
- * "own_transport_uri": "https://example.com/derec",
1610
- * "own_transport_protocol": 1,
1611
- * "own_transports": [{ "uri": "https://example.com/derec", "protocol": 0 }],
1718
+ * "own_transports": [{ "uri": "https://example.com/derec", "protocol": "https" }],
1612
1719
  * "threshold": 3,
1613
- * "keep_versions_count": 2,
1614
- * "timeout_in_secs": 30,
1615
1720
  * "auto_respond_on_failure": false,
1616
1721
  * "unpair_ack": 0,
1617
1722
  * "auto_reply_to": false,
@@ -1625,36 +1730,62 @@ struct ProcessPrePairResponseMessageResult process_pre_pair_response_message(con
1625
1730
  * "unpair": false,
1626
1731
  * "update_channel_info": false
1627
1732
  * },
1628
- * "remove_expired_channels": { "enabled": true, "timeout_in_secs": 300 },
1629
- * "replica_id": null
1733
+ * "timeouts": {
1734
+ * "inbound_message_secs": 300,
1735
+ * "sharing_round_secs": 60,
1736
+ * "unpair_ack_secs": 60,
1737
+ * "expired_channels": { "enabled": true, "timeout_in_secs": 300 }
1738
+ * },
1739
+ * "unsafe_connection": false,
1740
+ * "replica_id": null,
1741
+ * "parameter_range": {
1742
+ * "min_share_size": 0,
1743
+ * "max_share_size": 0,
1744
+ * "min_time_between_verifications": 0,
1745
+ * "max_time_between_verifications": 0,
1746
+ * "min_time_between_share_updates": 0,
1747
+ * "max_time_between_share_updates": 0,
1748
+ * "min_unresponsive_deletion_timeout": 0,
1749
+ * "max_unresponsive_deletion_timeout": 0,
1750
+ * "min_unresponsive_deactivation_timeout": 0,
1751
+ * "max_unresponsive_deactivation_timeout": 0
1752
+ * }
1630
1753
  * }
1631
1754
  * ```
1632
1755
  *
1633
1756
  * - `secret_id`: decimal-string `u64`.
1634
- * - `own_transport_uri`: may be `""` for the deferred-config path;
1635
- * `derec_protocol_set_own_transport` must be called before pairing
1636
- * in that case.
1637
- * - `own_transport_protocol`: [`derec_proto::Protocol`] discriminant.
1638
1757
  * - `own_transports`: every endpoint this application serves, in
1639
1758
  * preference order — the order decides which of a peer's offered
1640
- * endpoints is used. Optional; when non-empty it takes precedence over
1641
- * `own_transport_uri` / `own_transport_protocol`, which stay fully
1642
- * supported for callers that serve a single transport.
1643
- * - `threshold` / `keep_versions_count`: optional; omitted means
1644
- * [`crate::protocol::DEFAULT_THRESHOLD`] /
1645
- * [`crate::protocol::DEFAULT_KEEP_VERSIONS_COUNT`].
1646
- * - `unpair_ack`: `0` = Required, `1` = NotRequired; optional, omitted
1647
- * means `0`.
1648
- * - `auto_respond_on_failure` / `auto_reply_to`: optional, omitted means
1649
- * `false`.
1650
- * - `auto_accept`: one boolean per flow; the whole object is optional,
1651
- * omitted means every flow `false`.
1652
- * - `remove_expired_channels`: automatic removal of expired `Pending`
1653
- * channels. Optional — omitted means `{ "enabled": true,
1654
- * "timeout_in_secs": 300 }`. Both fields are always sent; when
1655
- * `enabled` is `false` the timeout is ignored by the library.
1759
+ * endpoints is used. `protocol` is its name (`"https"`, `"grpc"`) or
1760
+ * its `derec_proto::Protocol` discriminant (`0`, `1`). Each entry is validated, and
1761
+ * two entries of the same protocol are rejected. Required: an absent or
1762
+ * empty list is refused, since a node with no endpoint cannot be reached
1763
+ * by any peer.
1764
+ * - `threshold`: omitted means [`crate::protocol::DEFAULT_THRESHOLD`]. A
1765
+ * `threshold` below `2` is rejected with `DEREC_CODE_INVALID_INPUT`.
1766
+ * - `unpair_ack`: `0` / `"required"` = Required, `1` / `"not_required"` =
1767
+ * NotRequired; omitted means Required.
1768
+ * Any other value is rejected with `DEREC_CODE_FFI_INVALID_ENUM`.
1769
+ * - `auto_respond_on_failure` / `auto_reply_to`: omitted means `false`.
1770
+ * - `timeouts`: the four waiting periods, in whole seconds. Each field is
1771
+ * optional and omitted means the [`crate::protocol::types::Timeouts`]
1772
+ * default. `expired_channels` is the automatic removal of `Pending`
1773
+ * channels; both of its fields are always sent, and when `enabled` is
1774
+ * `false` the timeout is ignored by the library.
1775
+ * - `unsafe_connection`: accept plaintext `http://` and `grpc://`
1776
+ * endpoints. Development only; omitted means `false`.
1656
1777
  * - `replica_id`: decimal-string `u64`, or absent/`null` for "no
1657
1778
  * replica id".
1779
+ * - `auto_accept`: each flow is individually optional; an omitted flow
1780
+ * takes [`crate::protocol::AutoAcceptPolicy::default()`]'s value.
1781
+ * - `parameter_range`: the bounds advertised during pair negotiation,
1782
+ * mirroring [`derec_proto::ParameterRange`]. Optional object; every bound
1783
+ * is optional, omitted means `0` ("no constraint"), and each may be a JSON
1784
+ * number or a decimal string.
1785
+ * - `communication_info`: optional flat JSON object of string keys to
1786
+ * string values. Mutually exclusive with a non-empty
1787
+ * `communication_info_ptr` buffer; omitted with an empty buffer means
1788
+ * no entries.
1658
1789
  *
1659
1790
  * # Safety
1660
1791
  *
@@ -1720,46 +1851,15 @@ struct DeRecError derec_protocol_set_communication_info(struct DeRecProtocolHand
1720
1851
  const uint8_t *info_json_ptr,
1721
1852
  size_t info_json_len);
1722
1853
 
1723
- /**
1724
- * Replace this node's endpoint **for one protocol**, leaving the others
1725
- * alone. A node serves at most one endpoint per protocol, so the `(uri,
1726
- * protocol)` pair identifies the entry it replaces; an entry for a protocol
1727
- * not yet served is appended, and a replaced one keeps its position in the
1728
- * preference order. See
1729
- * [`crate::protocol::DeRecProtocol::set_own_transport`] for the changeover
1730
- * discipline (keep the old endpoint up during the transition).
1731
- *
1732
- * Superseded by [`derec_protocol_set_own_transports`], which takes the
1733
- * whole preference list and is the only way to change *which* protocols
1734
- * this node serves, or their order.
1735
- *
1736
- * # Safety
1737
- *
1738
- * `handle` must be a valid pointer returned by
1739
- * [`super::derec_protocol_new`]. `uri_ptr`/`uri_len` must describe a
1740
- * readable byte range. The `(uri, protocol)` pair is validated via
1741
- * [`super::validate_transport`] before it is stored — see that
1742
- * function's docs for the structural rules (length cap, scheme
1743
- * match, enum discriminant). Concurrent calls on the same handle
1744
- * from different threads are safe: the handle's internal mutex
1745
- * serializes them.
1746
- */
1747
- __attribute__((deprecated("use derec_protocol_set_own_transports, which takes the whole preference list; removed at 0.0.5")))
1748
- struct DeRecError derec_protocol_set_own_transport(struct DeRecProtocolHandle *handle,
1749
- const uint8_t *uri_ptr,
1750
- size_t uri_len,
1751
- int32_t protocol);
1752
-
1753
1854
  /**
1754
1855
  * Replace every endpoint this node advertises, in preference order.
1755
1856
  *
1756
1857
  * The runtime counterpart to the `own_transports` array accepted by
1757
- * [`super::derec_protocol_new`], and the way to change the whole set:
1758
- * `derec_protocol_set_own_transport` replaces only the entry for the
1759
- * protocol its URI names. A device serves at most one endpoint per
1858
+ * [`super::derec_protocol_new`]. A device serves at most one endpoint per
1760
1859
  * protocol, so this list is a preference order over distinct protocols and
1761
1860
  * two entries of the same protocol are rejected. Body is the same JSON
1762
- * shape that config array uses — `[{"uri": "...", "protocol": 0}, ...]`.
1861
+ * shape that config array uses — `[{"uri": "...", "protocol": "https"}, ...]`,
1862
+ * with `protocol` as its name or its `derec_proto::Protocol` discriminant.
1763
1863
  *
1764
1864
  * Every entry is validated before any is stored, so a malformed URI
1765
1865
  * leaves the previous set intact rather than half-applied.
@@ -1809,7 +1909,8 @@ struct DeRecProtocolEventsResult derec_protocol_start(struct DeRecProtocolHandle
1809
1909
 
1810
1910
  /**
1811
1911
  * Process an inbound `DeRecMessage` envelope. See
1812
- * [`crate::protocol::DeRecProtocol::process`].
1912
+ * [`crate::protocol::DeRecProtocol::process`]. On failure the result still
1913
+ * carries the events produced before it and the failing channel.
1813
1914
  *
1814
1915
  * # Safety
1815
1916
  *
@@ -1817,9 +1918,9 @@ struct DeRecProtocolEventsResult derec_protocol_start(struct DeRecProtocolHandle
1817
1918
  * [`super::derec_protocol_new`]. `message_ptr`/`message_len` must
1818
1919
  * describe a readable byte range.
1819
1920
  */
1820
- struct DeRecProtocolEventsResult derec_protocol_process(struct DeRecProtocolHandle *handle,
1821
- const uint8_t *message_ptr,
1822
- size_t message_len);
1921
+ struct DeRecProtocolProcessResult derec_protocol_process(struct DeRecProtocolHandle *handle,
1922
+ const uint8_t *message_ptr,
1923
+ size_t message_len);
1823
1924
 
1824
1925
  /**
1825
1926
  * Advance time-driven state without an inbound message. See
@@ -1883,13 +1984,15 @@ struct DeRecError derec_protocol_reject(struct DeRecProtocolHandle *handle,
1883
1984
  * {
1884
1985
  * "version": 7,
1885
1986
  * "recovered_secret": {
1886
- * "helpers": [{ "channel_id": "11", "transport_uri": "...",
1987
+ * "helpers": [{ "channel_id": "11",
1988
+ * "transports": [{ "uri": "https://helper.example", "protocol": "https" }],
1887
1989
  * "shared_key": [..32 bytes..],
1888
1990
  * "communication_info": {} }],
1889
1991
  * "secrets": [{ "id": [..], "name": "...", "data": [..] }],
1890
1992
  * "replicas": {
1891
1993
  * "channel_id": "21",
1892
- * "members": [{ "replica_id": "51966", "transport_uri": "...",
1994
+ * "members": [{ "replica_id": "51966",
1995
+ * "transports": [{ "uri": "https://replica.example", "protocol": "https" }],
1893
1996
  * "role": "Source", "communication_info": {} }],
1894
1997
  * "shared_key": [..32 bytes..]
1895
1998
  * }
@@ -1898,8 +2001,14 @@ struct DeRecError derec_protocol_reject(struct DeRecProtocolHandle *handle,
1898
2001
  * ```
1899
2002
  *
1900
2003
  * Field names mirror `SecretWire` in `protocol/events/wire.rs` — the
1901
- * same shape `SecretRecovered` carries. `channel_id` and `replica_id`
1902
- * are decimal `u64` strings (empty / absent means zero).
2004
+ * same shape `SecretRecovered` carries, so the `secret` of that event can
2005
+ * be passed back unchanged. `channel_id` and `replica_id` are required
2006
+ * decimal `u64` strings; an absent, empty, or malformed id is rejected.
2007
+ * `transports` is every endpoint the peer advertised, in its preference
2008
+ * order; `protocol` is its name, `"https"` or `"grpc"`. An absent or
2009
+ * `null` `transports` reads as an empty list, and a helper or member with
2010
+ * no endpoint gets no channel: restore skips it and reports it as a
2011
+ * `PeerNotRestored` event. `role` is `"Source"` or `"Destination"`.
1903
2012
  *
1904
2013
  * `replicas` is an **object**, not an array, and is omitted entirely when
1905
2014
  * the `secret_id` has no replica group. Every member of the group shares
@@ -1907,15 +2016,23 @@ struct DeRecError derec_protocol_reject(struct DeRecProtocolHandle *handle,
1907
2016
  * repeated per member; a member is identified by `replica_id` alone, and
1908
2017
  * the group's source is the member whose `role` is `"Source"`.
1909
2018
  *
2019
+ * On success the result carries the events the restore produced, as
2020
+ * [`DeRecProtocolEventsResult`] does — one `PeerNotRestored` per skipped
2021
+ * helper or member among them. When channels already exist at ids
2022
+ * restore is about to write, `error.code` is
2023
+ * `DEREC_CODE_RESTORE_CONFLICT` and `conflicting_channel_ids_json` lists
2024
+ * exactly those ids; nothing is written, so the application can clear
2025
+ * them and retry.
2026
+ *
1910
2027
  * # Safety
1911
2028
  *
1912
2029
  * `handle` must be a valid pointer returned by
1913
2030
  * [`super::derec_protocol_new`]. `params_json_ptr`/`params_json_len`
1914
2031
  * must describe a readable byte range.
1915
2032
  */
1916
- struct DeRecProtocolEventsResult derec_protocol_restore(struct DeRecProtocolHandle *handle,
1917
- const uint8_t *params_json_ptr,
1918
- size_t params_json_len);
2033
+ struct DeRecProtocolRestoreResult derec_protocol_restore(struct DeRecProtocolHandle *handle,
2034
+ const uint8_t *params_json_ptr,
2035
+ size_t params_json_len);
1919
2036
 
1920
2037
  /**
1921
2038
  * Derive the human-readable fingerprint for a paired channel. See
@@ -1985,6 +2102,48 @@ struct DeRecProtocolCreateContactResult derec_protocol_create_contact(struct DeR
1985
2102
  uint32_t has_nonce,
1986
2103
  uint64_t nonce);
1987
2104
 
2105
+ /**
2106
+ * Decode the `endpoints` buffer a [`TransportCallbacks::send`] call
2107
+ * receives into a UTF-8 JSON array of `{"protocol": "...", "uri": "..."}`
2108
+ * objects, order preserved. `protocol` is the name
2109
+ * [`crate::interop::ffi::protocol_names::derec_transport_protocol_name`] gives the
2110
+ * discriminant, the same endpoint shape the WASM SDKs hand to their
2111
+ * transport's `send`. An entry with an unknown protocol discriminant, or a
2112
+ * malformed buffer, is an error rather than a guessed name.
2113
+ *
2114
+ * Release `bytes` with [`crate::interop::ffi::common::derec_free_buffer`].
2115
+ *
2116
+ * # Safety
2117
+ *
2118
+ * `endpoints_ptr` must be valid for reads of `endpoints_len` bytes when
2119
+ * `endpoints_len` is non-zero.
2120
+ */
2121
+ struct DeRecMessageJsonResult derec_transport_endpoints_json(const uint8_t *endpoints_ptr,
2122
+ size_t endpoints_len);
2123
+
2124
+ /**
2125
+ * Static NUL-terminated name (`"https"` or `"grpc"`) for a
2126
+ * `derec_proto::Protocol` discriminant.
2127
+ *
2128
+ * Returns NULL for a discriminant outside the defined protocols. The
2129
+ * returned pointer has static lifetime and must **not** be freed.
2130
+ */
2131
+ const char *derec_transport_protocol_name(int32_t protocol);
2132
+
2133
+ /**
2134
+ * `derec_proto::Protocol` discriminant for a protocol name, compared
2135
+ * case-insensitively.
2136
+ *
2137
+ * Returns `-1` when the name is not valid UTF-8 or names no defined
2138
+ * protocol.
2139
+ *
2140
+ * # Safety
2141
+ *
2142
+ * `name_ptr`/`name_len` must describe a readable byte range, or `name_len`
2143
+ * must be zero.
2144
+ */
2145
+ int32_t derec_transport_protocol_discriminant(const uint8_t *name_ptr, size_t name_len);
2146
+
1988
2147
  struct DeRecProtocolVersion derec_protocol_version(void);
1989
2148
 
1990
2149
  /**
@@ -2053,6 +2212,13 @@ struct RecoverFromShareResponsesResult recover_from_share_responses(const uint8_
2053
2212
  uint64_t secret_id,
2054
2213
  uint32_t version);
2055
2214
 
2215
+ /**
2216
+ * A fresh replica identity. See [`crate::generate_replica_id`]: the caller
2217
+ * persists it once per device and passes the same value on every protocol
2218
+ * init. Never `0`.
2219
+ */
2220
+ uint64_t derec_generate_replica_id(void);
2221
+
2056
2222
  /**
2057
2223
  * # Safety
2058
2224
  *