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
//! The `TrustRegistry` referral a community publishes in its DID document.
//!
//! TRQP v2.0 recommends that a trust registry be machine-discoverable *via the
//! `authority_id`* — that is, from the DID document of the authority whose
//! records it holds:
//!
//! > "It is RECOMMENDED that the TRQP service endpoint(s) for any authoritative
//! > trust registry be machine-discoverable via the `authority_id`. An example
//! > would be to publish either of the following in the DID document for the
//! > `authority_id`: 1. The authoritative TRQP service endpoint URL(s).
//! > 2. The DID(s) identifying authoritative trust registries."
//!
//! A VTC *is* the `authority_id` in every trust tuple evaluated under it, so
//! this entry belongs in the VTC's document and names the registry by DID —
//! form 2. A client that holds only the community's DID can then resolve one
//! hop to the registry rather than being configured with both.
//!
//! ## Referral, not endpoint
//!
//! The same `TrustRegistry` service type means two different things depending
//! on whose document carries it, and the endpoint's shape is what distinguishes
//! them: in a *registry's own* document the `uri` is that registry's TRQP URL
//! (an **endpoint**); in a *community's* document the `uri` is the registry's
//! **DID** (a **referral**). Consumers test the `uri` for a `did:` prefix —
//! `trql_client::registry_referral` is the reference implementation — so a
//! referral whose `uri` is an https URL would be silently misread as this
//! community claiming to serve TRQP itself. [`referral_service`] refuses a
//! non-DID `uri` for that reason.
//!
//! ## A referral asserts nothing about authority
//!
//! Publishing this entry is a self-assertion: anyone may name any registry in
//! their own document. Authority flows registry → subject, never the reverse,
//! so a client that follows the referral must confirm the registry answers
//! *for* the community that referred it before believing the answer. That
//! check lives client-side (`TrqlClient::referred_by`), not here — emitting
//! the entry establishes where to ask and nothing more.
use ;
use TemplateError;
/// DID-document service `type` for a Trust Registry, per the
/// [ToIP Trust Registry Service Profile][profile].
///
/// TRQP v2.0 blesses the indirection but names no service type, so this one
/// comes from the Service Profile spec instead. That spec is Pre-Draft 0.0.1
/// and self-described as non-binding — the value is pinned here so the whole
/// stack moves together if it changes.
///
/// [profile]: https://github.com/trustoverip/tswg-trust-registry-service-profile/blob/main/spec.md
pub const TRUST_REGISTRY_SERVICE_TYPE: &str = "TrustRegistry";
/// Profile URI stamped on the referral's service endpoint.
///
/// The ToIP Service Profile example says `.../profiles/trp/v2`, left over from
/// when the protocol was still called TRP. Neither URI resolves today, so
/// nothing depends on the choice; we name the protocol as it is now called,
/// matching `trust-registry`'s own `TRQP_PROFILE_URI`. A consumer that starts
/// matching on `profile` would need to accept both spellings.
pub const TRQP_PROFILE_URI: &str = "https://trustoverip.org/profiles/trqp/v2";
/// Template variable carrying the whole referral entry.
///
/// The `vtc-host` template declares this in `optionalVars` with a `null`
/// default and places it as a bare array member, so a community provisioned
/// without a registry simply has the element pruned (see the array arm of
/// `render::substitute_value`). That null-pruning slot is the only conditional
/// mechanism the template format has, and it is why the entry is built here in
/// Rust rather than spelled out in the template JSON: the template cannot own
/// a constant it can also omit.
pub const TRUST_REGISTRY_SERVICE_VAR: &str = "SERVICE_TRUST_REGISTRY";
/// Fragment appended to the community's DID to id the referral entry.
const SERVICE_ID_FRAGMENT: &str = "#trust-registry";
/// Build the `TrustRegistry` referral entry naming `registry_did` as the
/// registry authoritative for this community.
///
/// Pass the result as [`TRUST_REGISTRY_SERVICE_VAR`] in a template's vars map.
/// The `id` carries the literal `{DID}` sentinel: caller-supplied variable
/// values are not re-substituted by the renderer, but `didwebvh-rs` resolves
/// `{DID}` across every leaf string after the SCID is computed, so the entry
/// lands with the community's minted DID.
///
/// # Errors
///
/// [`TemplateError::Invalid`] if `registry_did` is not a DID. An https URL
/// here would render an entry that reads as an *endpoint* — this community
/// advertising a TRQP surface it does not serve — rather than a referral.