@derec-alliance/react-native 0.0.5 → 0.0.6

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.
@@ -296,18 +296,6 @@
296
296
  */
297
297
  #define DEREC_CODE_NO_USABLE_ENDPOINT 121
298
298
 
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
299
  /**
312
300
  * Discriminants selecting which message [`derec_decode_message_json`] and
313
301
  * [`derec_encode_message_json`] operate on.
@@ -678,6 +666,20 @@ typedef struct ExtractPrePairResponseResult {
678
666
  struct DeRecBuffer response_proto_bytes;
679
667
  } ExtractPrePairResponseResult;
680
668
 
669
+ typedef struct ProducePrePairNoKeysResponseMessageResult {
670
+ struct DeRecError error;
671
+ /**
672
+ * Serialized outer plaintext `DeRecMessage` envelope carrying a
673
+ * `PrePairResponseMessage` with freshly generated public keys.
674
+ */
675
+ struct DeRecBuffer envelope_wire_bytes;
676
+ /**
677
+ * The secret key material generated for this pairing. The caller MUST
678
+ * persist it: the `PairRequestMessage` that follows is encrypted to it.
679
+ */
680
+ struct DeRecBuffer secret_key_material;
681
+ } ProducePrePairNoKeysResponseMessageResult;
682
+
681
683
  /**
682
684
  * On success the two key buffers hold the validated public keys republished
683
685
  * by the contact creator. On failure (status non-Ok, hash mismatch, etc.)
@@ -693,6 +695,15 @@ typedef struct ProcessPrePairResponseMessageResult {
693
695
  uint64_t nonce;
694
696
  } ProcessPrePairResponseMessageResult;
695
697
 
698
+ typedef struct PairingFingerprintResult {
699
+ struct DeRecError error;
700
+ /**
701
+ * NUL-terminated UTF-8 fingerprint. Free with
702
+ * [`crate::interop::ffi::common::derec_free_string`]. Null on failure.
703
+ */
704
+ char *fingerprint;
705
+ } PairingFingerprintResult;
706
+
696
707
  /**
697
708
  * Result type for [`derec_protocol_new`].
698
709
  */
@@ -796,6 +807,12 @@ typedef struct ChannelStoreCallbacks {
796
807
 
797
808
  /**
798
809
  * Caller-supplied callbacks for secret persistence.
810
+ *
811
+ * `load_many` receives the requested channel ids as a JSON array of
812
+ * numbers and returns a JSON array with exactly one entry per requested id,
813
+ * in the same order: the same `{kind, bytes}` record `load` returns, or
814
+ * `null` when nothing of `kind` is stored for that channel. Whether a
815
+ * missing entry is an error is decided by the library, not the callback.
799
816
  */
800
817
  typedef struct SecretStoreCallbacks {
801
818
  void *user_data;
@@ -805,6 +822,13 @@ typedef struct SecretStoreCallbacks {
805
822
  uint32_t kind,
806
823
  uint8_t **out_ptr,
807
824
  size_t *out_len);
825
+ int32_t (*load_many)(void *user_data,
826
+ uint64_t secret_id,
827
+ const uint8_t *channel_ids_json_ptr,
828
+ size_t channel_ids_json_len,
829
+ uint32_t kind,
830
+ uint8_t **out_ptr,
831
+ size_t *out_len);
808
832
  int32_t (*save)(void *user_data,
809
833
  uint64_t secret_id,
810
834
  uint64_t channel_id,
@@ -977,6 +1001,27 @@ typedef struct DeRecProtocolEventsResult {
977
1001
  struct DeRecBuffer events_json;
978
1002
  } DeRecProtocolEventsResult;
979
1003
 
1004
+ /**
1005
+ * Result of [`derec_protocol_restore`].
1006
+ *
1007
+ * A restore refused because channels already exist at the recovered ids
1008
+ * reports which ones in `conflicting_channel_ids_json`, so the application
1009
+ * can clear exactly those and retry.
1010
+ */
1011
+ typedef struct DeRecProtocolRestoreResult {
1012
+ struct DeRecError error;
1013
+ /**
1014
+ * UTF-8 JSON array of events, as on [`DeRecProtocolEventsResult`].
1015
+ */
1016
+ struct DeRecBuffer events_json;
1017
+ /**
1018
+ * UTF-8 JSON array of decimal-string channel ids. Non-empty only when
1019
+ * `error.code` is `DEREC_CODE_RESTORE_CONFLICT`. Caller releases via
1020
+ * [`crate::interop::ffi::derec_free_buffer`].
1021
+ */
1022
+ struct DeRecBuffer conflicting_channel_ids_json;
1023
+ } DeRecProtocolRestoreResult;
1024
+
980
1025
  /**
981
1026
  * Result type for fingerprint accessors.
982
1027
  */
@@ -1372,34 +1417,9 @@ struct CreateContactMessageResult create_contact_message(uint64_t channel_id,
1372
1417
  uint32_t has_nonce,
1373
1418
  uint64_t nonce);
1374
1419
 
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
1420
  /**
1399
1421
  * 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.
1422
+ * See [`crate::primitives::pairing::request::encode_contact`].
1403
1423
  *
1404
1424
  * The JSON shape is [`ContactMessageDto`]'s serde representation with one
1405
1425
  * adjustment applied at this seam: `channel_id` and `nonce` are decimal
@@ -1419,9 +1439,7 @@ struct EncodeContactMessageResult encode_contact_message(const uint8_t *contact_
1419
1439
  /**
1420
1440
  * Decodes proto-encoded `ContactMessage` wire bytes into the JSON
1421
1441
  * [`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.
1442
+ * See [`crate::primitives::pairing::request::decode_contact`].
1425
1443
  *
1426
1444
  * # Safety
1427
1445
  *
@@ -1452,6 +1470,10 @@ struct ProducePairRequestMessageResult produce_pair_request_message(int32_t send
1452
1470
  size_t parameter_range_len);
1453
1471
 
1454
1472
  /**
1473
+ * `parameter_range_ptr` may be null / zero-length to declare no parameter
1474
+ * range; otherwise it must be serialized [`derec_proto::ParameterRange`]
1475
+ * proto bytes. See [`crate::primitives::pairing::request::extract`].
1476
+ *
1455
1477
  * # Safety
1456
1478
  *
1457
1479
  * Non-null input pointers must point to the corresponding readable byte ranges.
@@ -1459,7 +1481,9 @@ struct ProducePairRequestMessageResult produce_pair_request_message(int32_t send
1459
1481
  struct ExtractPairRequestResult extract_pair_request(const uint8_t *request_ptr,
1460
1482
  size_t request_len,
1461
1483
  const uint8_t *secret_key_material_ptr,
1462
- size_t secret_key_material_len);
1484
+ size_t secret_key_material_len,
1485
+ const uint8_t *parameter_range_ptr,
1486
+ size_t parameter_range_len);
1463
1487
 
1464
1488
  /**
1465
1489
  * `request_proto_ptr` / `request_proto_len` must be the `request_proto_bytes`
@@ -1499,6 +1523,10 @@ struct ExtractPairResponseResult extract_pair_response(const uint8_t *response_p
1499
1523
  * `response_proto_ptr` / `response_proto_len` must be the
1500
1524
  * `response_proto_bytes` returned by [`extract_pair_response`].
1501
1525
  *
1526
+ * `parameter_range_ptr` may be null / zero-length to declare no parameter
1527
+ * range; otherwise it must be serialized [`derec_proto::ParameterRange`]
1528
+ * proto bytes. See [`crate::primitives::pairing::response::process`].
1529
+ *
1502
1530
  * # Safety
1503
1531
  *
1504
1532
  * Non-null input pointers must point to the corresponding readable byte ranges.
@@ -1508,7 +1536,9 @@ struct ProcessPairResponseMessageResult process_pair_response_message(const uint
1508
1536
  const uint8_t *response_proto_ptr,
1509
1537
  size_t response_proto_len,
1510
1538
  const uint8_t *secret_key_material_ptr,
1511
- size_t secret_key_material_len);
1539
+ size_t secret_key_material_len,
1540
+ const uint8_t *parameter_range_ptr,
1541
+ size_t parameter_range_len);
1512
1542
 
1513
1543
  /**
1514
1544
  * Builds a plaintext `PrePairRequestMessage` envelope. Used by the scanner
@@ -1563,6 +1593,55 @@ struct ProducePrePairResponseMessageResult produce_pre_pair_response_message(uin
1563
1593
  struct ExtractPrePairResponseResult extract_pre_pair_response(const uint8_t *envelope_ptr,
1564
1594
  size_t envelope_len);
1565
1595
 
1596
+ /**
1597
+ * Contact-creator side of a `NO_KEYS` pairing: generates key material and
1598
+ * answers the `PrePairRequestMessage` with its public half.
1599
+ *
1600
+ * The caller MUST first match the request's `nonce` against the contact it
1601
+ * issued — the only thing that authenticates a `NO_KEYS` request — and MUST
1602
+ * keep the resulting channel unusable until both sides confirm
1603
+ * [`pairing_fingerprint`] out of band.
1604
+ *
1605
+ * `request_proto_ptr` / `request_proto_len` must be the `request_proto_bytes`
1606
+ * returned by [`extract_pre_pair_request`].
1607
+ *
1608
+ * # Safety
1609
+ *
1610
+ * Non-null input pointers must point to the corresponding readable byte ranges.
1611
+ */
1612
+ struct ProducePrePairNoKeysResponseMessageResult produce_pre_pair_no_keys_response_message(uint64_t channel_id,
1613
+ const uint8_t *request_proto_ptr,
1614
+ size_t request_proto_len);
1615
+
1616
+ /**
1617
+ * Scanner side of a `NO_KEYS` pairing: accepts the contact creator's public
1618
+ * keys. There is no binding hash to check them against, so the channel this
1619
+ * leads to MUST stay unusable until both sides confirm
1620
+ * [`pairing_fingerprint`] out of band.
1621
+ *
1622
+ * `response_proto_ptr` / `response_proto_len` must be the
1623
+ * `response_proto_bytes` returned by [`extract_pre_pair_response`].
1624
+ *
1625
+ * # Safety
1626
+ *
1627
+ * Non-null input pointers must point to the corresponding readable byte ranges.
1628
+ */
1629
+ struct ProcessPrePairResponseMessageResult process_pre_pair_no_keys_response_message(const uint8_t *contact_message_ptr,
1630
+ size_t contact_message_len,
1631
+ const uint8_t *response_proto_ptr,
1632
+ size_t response_proto_len);
1633
+
1634
+ /**
1635
+ * The human-readable fingerprint of a pairing's shared key. See
1636
+ * [`crate::primitives::pairing::fingerprint`].
1637
+ *
1638
+ * # Safety
1639
+ *
1640
+ * `shared_key_ptr` must point to `shared_key_len` readable bytes.
1641
+ */
1642
+ struct PairingFingerprintResult pairing_fingerprint(const uint8_t *shared_key_ptr,
1643
+ size_t shared_key_len);
1644
+
1566
1645
  /**
1567
1646
  * Scanner-side: validates a decoded `PrePairResponseMessage` against the
1568
1647
  * original `ContactMessage`'s SHA-384 binding hash. On success returns the
@@ -1588,30 +1667,25 @@ struct ProcessPrePairResponseMessageResult process_pre_pair_response_message(con
1588
1667
  * arguments in a single call — e.g. Go via `purego` (no cgo), which
1589
1668
  * panics with "too many stack arguments" past a handful of
1590
1669
  * 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
1670
+ * buffer; `communication_info` travels either in that JSON or as a
1671
+ * separate proto-encoded `CommunicationInfo` buffer; the 6 store/transport callback structs
1593
1672
  * are still passed as individual pointers, since purego marshals
1594
1673
  * pointer-sized arguments natively.
1595
1674
  *
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
1675
+ * `config_json` must deserialize to [`ProtocolConfig`] — all field names
1676
+ * `snake_case`. `secret_id` is the only required field. Every other field
1677
+ * may be omitted, in which case the value matches
1602
1678
  * [`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:
1679
+ * setting: [`ProtocolConfig`]'s field-level `#[serde(default)]` attributes
1680
+ * read the same constants the builder does. A field that is present is
1681
+ * used as given, including `0`, and validated by the builder.
1605
1682
  *
1606
1683
  * ```json
1607
1684
  * {
1608
1685
  * "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 }],
1686
+ * "own_transports": [{ "uri": "https://example.com/derec", "protocol": "https" }],
1612
1687
  * "threshold": 3,
1613
- * "keep_versions_count": 2,
1614
- * "timeout_in_secs": 30,
1688
+ * "keep_versions_count": 3,
1615
1689
  * "auto_respond_on_failure": false,
1616
1690
  * "unpair_ack": 0,
1617
1691
  * "auto_reply_to": false,
@@ -1625,36 +1699,64 @@ struct ProcessPrePairResponseMessageResult process_pre_pair_response_message(con
1625
1699
  * "unpair": false,
1626
1700
  * "update_channel_info": false
1627
1701
  * },
1628
- * "remove_expired_channels": { "enabled": true, "timeout_in_secs": 300 },
1629
- * "replica_id": null
1702
+ * "timeouts": {
1703
+ * "inbound_message_secs": 300,
1704
+ * "sharing_round_secs": 60,
1705
+ * "unpair_ack_secs": 60,
1706
+ * "expired_channels": { "enabled": true, "timeout_in_secs": 300 }
1707
+ * },
1708
+ * "unsafe_connection": false,
1709
+ * "replica_id": null,
1710
+ * "parameter_range": {
1711
+ * "min_share_size": 0,
1712
+ * "max_share_size": 0,
1713
+ * "min_time_between_verifications": 0,
1714
+ * "max_time_between_verifications": 0,
1715
+ * "min_time_between_share_updates": 0,
1716
+ * "max_time_between_share_updates": 0,
1717
+ * "min_unresponsive_deletion_timeout": 0,
1718
+ * "max_unresponsive_deletion_timeout": 0,
1719
+ * "min_unresponsive_deactivation_timeout": 0,
1720
+ * "max_unresponsive_deactivation_timeout": 0
1721
+ * }
1630
1722
  * }
1631
1723
  * ```
1632
1724
  *
1633
1725
  * - `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
1726
  * - `own_transports`: every endpoint this application serves, in
1639
1727
  * 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
1728
+ * endpoints is used. `protocol` is its name (`"https"`, `"grpc"`) or
1729
+ * its `derec_proto::Protocol` discriminant (`0`, `1`). Each entry is validated, and
1730
+ * two entries of the same protocol are rejected. Required: an absent or
1731
+ * empty list is refused, since a node with no endpoint cannot be reached
1732
+ * by any peer.
1733
+ * - `threshold` / `keep_versions_count`: omitted means
1644
1734
  * [`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.
1735
+ * [`crate::protocol::DEFAULT_KEEP_VERSIONS_COUNT`]. A `threshold` below
1736
+ * `2` is rejected with `DEREC_CODE_INVALID_INPUT`.
1737
+ * - `unpair_ack`: `0` / `"required"` = Required, `1` / `"not_required"` =
1738
+ * NotRequired; omitted means Required.
1739
+ * Any other value is rejected with `DEREC_CODE_FFI_INVALID_ENUM`.
1740
+ * - `auto_respond_on_failure` / `auto_reply_to`: omitted means `false`.
1741
+ * - `timeouts`: the four waiting periods, in whole seconds. Each field is
1742
+ * optional and omitted means the [`crate::protocol::types::Timeouts`]
1743
+ * default. `expired_channels` is the automatic removal of `Pending`
1744
+ * channels; both of its fields are always sent, and when `enabled` is
1745
+ * `false` the timeout is ignored by the library.
1746
+ * - `unsafe_connection`: accept plaintext `http://` and `grpc://`
1747
+ * endpoints. Development only; omitted means `false`.
1656
1748
  * - `replica_id`: decimal-string `u64`, or absent/`null` for "no
1657
1749
  * replica id".
1750
+ * - `auto_accept`: each flow is individually optional; an omitted flow
1751
+ * takes [`crate::protocol::AutoAcceptPolicy::default()`]'s value.
1752
+ * - `parameter_range`: the bounds advertised during pair negotiation,
1753
+ * mirroring [`derec_proto::ParameterRange`]. Optional object; every bound
1754
+ * is optional, omitted means `0` ("no constraint"), and each may be a JSON
1755
+ * number or a decimal string.
1756
+ * - `communication_info`: optional flat JSON object of string keys to
1757
+ * string values. Mutually exclusive with a non-empty
1758
+ * `communication_info_ptr` buffer; omitted with an empty buffer means
1759
+ * no entries.
1658
1760
  *
1659
1761
  * # Safety
1660
1762
  *
@@ -1720,46 +1822,15 @@ struct DeRecError derec_protocol_set_communication_info(struct DeRecProtocolHand
1720
1822
  const uint8_t *info_json_ptr,
1721
1823
  size_t info_json_len);
1722
1824
 
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
1825
  /**
1754
1826
  * Replace every endpoint this node advertises, in preference order.
1755
1827
  *
1756
1828
  * 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
1829
+ * [`super::derec_protocol_new`]. A device serves at most one endpoint per
1760
1830
  * protocol, so this list is a preference order over distinct protocols and
1761
1831
  * two entries of the same protocol are rejected. Body is the same JSON
1762
- * shape that config array uses — `[{"uri": "...", "protocol": 0}, ...]`.
1832
+ * shape that config array uses — `[{"uri": "...", "protocol": "https"}, ...]`,
1833
+ * with `protocol` as its name or its `derec_proto::Protocol` discriminant.
1763
1834
  *
1764
1835
  * Every entry is validated before any is stored, so a malformed URI
1765
1836
  * leaves the previous set intact rather than half-applied.
@@ -1883,13 +1954,15 @@ struct DeRecError derec_protocol_reject(struct DeRecProtocolHandle *handle,
1883
1954
  * {
1884
1955
  * "version": 7,
1885
1956
  * "recovered_secret": {
1886
- * "helpers": [{ "channel_id": "11", "transport_uri": "...",
1957
+ * "helpers": [{ "channel_id": "11",
1958
+ * "transports": [{ "uri": "https://helper.example", "protocol": "https" }],
1887
1959
  * "shared_key": [..32 bytes..],
1888
1960
  * "communication_info": {} }],
1889
1961
  * "secrets": [{ "id": [..], "name": "...", "data": [..] }],
1890
1962
  * "replicas": {
1891
1963
  * "channel_id": "21",
1892
- * "members": [{ "replica_id": "51966", "transport_uri": "...",
1964
+ * "members": [{ "replica_id": "51966",
1965
+ * "transports": [{ "uri": "https://replica.example", "protocol": "https" }],
1893
1966
  * "role": "Source", "communication_info": {} }],
1894
1967
  * "shared_key": [..32 bytes..]
1895
1968
  * }
@@ -1898,8 +1971,14 @@ struct DeRecError derec_protocol_reject(struct DeRecProtocolHandle *handle,
1898
1971
  * ```
1899
1972
  *
1900
1973
  * 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).
1974
+ * same shape `SecretRecovered` carries, so the `secret` of that event can
1975
+ * be passed back unchanged. `channel_id` and `replica_id` are required
1976
+ * decimal `u64` strings; an absent, empty, or malformed id is rejected.
1977
+ * `transports` is every endpoint the peer advertised, in its preference
1978
+ * order; `protocol` is its name, `"https"` or `"grpc"`. An absent or
1979
+ * `null` `transports` reads as an empty list, and a helper or member with
1980
+ * no endpoint gets no channel: restore skips it and reports it as a
1981
+ * `PeerNotRestored` event. `role` is `"Source"` or `"Destination"`.
1903
1982
  *
1904
1983
  * `replicas` is an **object**, not an array, and is omitted entirely when
1905
1984
  * the `secret_id` has no replica group. Every member of the group shares
@@ -1907,15 +1986,23 @@ struct DeRecError derec_protocol_reject(struct DeRecProtocolHandle *handle,
1907
1986
  * repeated per member; a member is identified by `replica_id` alone, and
1908
1987
  * the group's source is the member whose `role` is `"Source"`.
1909
1988
  *
1989
+ * On success the result carries the events the restore produced, as
1990
+ * [`DeRecProtocolEventsResult`] does — one `PeerNotRestored` per skipped
1991
+ * helper or member among them. When channels already exist at ids
1992
+ * restore is about to write, `error.code` is
1993
+ * `DEREC_CODE_RESTORE_CONFLICT` and `conflicting_channel_ids_json` lists
1994
+ * exactly those ids; nothing is written, so the application can clear
1995
+ * them and retry.
1996
+ *
1910
1997
  * # Safety
1911
1998
  *
1912
1999
  * `handle` must be a valid pointer returned by
1913
2000
  * [`super::derec_protocol_new`]. `params_json_ptr`/`params_json_len`
1914
2001
  * must describe a readable byte range.
1915
2002
  */
1916
- struct DeRecProtocolEventsResult derec_protocol_restore(struct DeRecProtocolHandle *handle,
1917
- const uint8_t *params_json_ptr,
1918
- size_t params_json_len);
2003
+ struct DeRecProtocolRestoreResult derec_protocol_restore(struct DeRecProtocolHandle *handle,
2004
+ const uint8_t *params_json_ptr,
2005
+ size_t params_json_len);
1919
2006
 
1920
2007
  /**
1921
2008
  * Derive the human-readable fingerprint for a paired channel. See
@@ -1985,6 +2072,48 @@ struct DeRecProtocolCreateContactResult derec_protocol_create_contact(struct DeR
1985
2072
  uint32_t has_nonce,
1986
2073
  uint64_t nonce);
1987
2074
 
2075
+ /**
2076
+ * Decode the `endpoints` buffer a [`TransportCallbacks::send`] call
2077
+ * receives into a UTF-8 JSON array of `{"protocol": "...", "uri": "..."}`
2078
+ * objects, order preserved. `protocol` is the name
2079
+ * [`crate::interop::ffi::protocol_names::derec_transport_protocol_name`] gives the
2080
+ * discriminant, the same endpoint shape the WASM SDKs hand to their
2081
+ * transport's `send`. An entry with an unknown protocol discriminant, or a
2082
+ * malformed buffer, is an error rather than a guessed name.
2083
+ *
2084
+ * Release `bytes` with [`crate::interop::ffi::common::derec_free_buffer`].
2085
+ *
2086
+ * # Safety
2087
+ *
2088
+ * `endpoints_ptr` must be valid for reads of `endpoints_len` bytes when
2089
+ * `endpoints_len` is non-zero.
2090
+ */
2091
+ struct DeRecMessageJsonResult derec_transport_endpoints_json(const uint8_t *endpoints_ptr,
2092
+ size_t endpoints_len);
2093
+
2094
+ /**
2095
+ * Static NUL-terminated name (`"https"` or `"grpc"`) for a
2096
+ * `derec_proto::Protocol` discriminant.
2097
+ *
2098
+ * Returns NULL for a discriminant outside the defined protocols. The
2099
+ * returned pointer has static lifetime and must **not** be freed.
2100
+ */
2101
+ const char *derec_transport_protocol_name(int32_t protocol);
2102
+
2103
+ /**
2104
+ * `derec_proto::Protocol` discriminant for a protocol name, compared
2105
+ * case-insensitively.
2106
+ *
2107
+ * Returns `-1` when the name is not valid UTF-8 or names no defined
2108
+ * protocol.
2109
+ *
2110
+ * # Safety
2111
+ *
2112
+ * `name_ptr`/`name_len` must describe a readable byte range, or `name_len`
2113
+ * must be zero.
2114
+ */
2115
+ int32_t derec_transport_protocol_discriminant(const uint8_t *name_ptr, size_t name_len);
2116
+
1988
2117
  struct DeRecProtocolVersion derec_protocol_version(void);
1989
2118
 
1990
2119
  /**
@@ -2053,6 +2182,13 @@ struct RecoverFromShareResponsesResult recover_from_share_responses(const uint8_
2053
2182
  uint64_t secret_id,
2054
2183
  uint32_t version);
2055
2184
 
2185
+ /**
2186
+ * A fresh replica identity. See [`crate::generate_replica_id`]: the caller
2187
+ * persists it once per device and passes the same value on every protocol
2188
+ * init. Never `0`.
2189
+ */
2190
+ uint64_t derec_generate_replica_id(void);
2191
+
2056
2192
  /**
2057
2193
  * # Safety
2058
2194
  *