@keri-ts/hosting 2.2.1 → 2.2.2

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.
@@ -3,9 +3,10 @@
3
3
  *
4
4
  * The router is path-first: every endpoint classifier receives the same
5
5
  * normalized path plus hosted-prefix projections, so mailbox admin, OOBI, and
6
- * generic CESR ingress cannot disagree about which local AID a URL addresses.
6
+ * generic CESR ingress cannot disagree about which local AID a URL addresses. When several local AIDs share
7
+ * one URL, the request's `CESR-DESTINATION` picks among them.
7
8
  */
8
- import { resolveHostedEndpointPath } from "keri-ts/runtime";
9
+ import { CESR_DESTINATION_HEADER, resolveHostedEndpointPath, } from "keri-ts/runtime";
9
10
  import { parseOobiRouteRequest } from "./endpoints/oobi.js";
10
11
  /** Snapshot one request into the path-first routing context. */
11
12
  export function buildProtocolRequestContext(req, runtime, policy = {}) {
@@ -14,14 +15,17 @@ export function buildProtocolRequestContext(req, runtime, policy = {}) {
14
15
  // Resolve the same normalized path against the three hosted path shapes used
15
16
  // by the HTTP edge: plain hosted endpoints, mailbox admin endpoints, and
16
17
  // generic root-relative CESR ingress.
18
+ // Several local identifiers may advertise one ingress URL; the path then cannot tell them apart and the
19
+ // request's own destination does.
20
+ const destination = req.headers.get(CESR_DESTINATION_HEADER);
17
21
  const hosted = runtime
18
- ? resolveHostedEndpointPath(runtime.hby, pathname, "", policy.hostedPrefixes)
22
+ ? resolveHostedEndpointPath(runtime.hby, pathname, "", policy.hostedPrefixes, destination)
19
23
  : null;
20
24
  const mailboxAdmin = runtime
21
- ? resolveHostedEndpointPath(runtime.hby, pathname, "/mailboxes", policy.hostedPrefixes)
25
+ ? resolveHostedEndpointPath(runtime.hby, pathname, "/mailboxes", policy.hostedPrefixes, destination)
22
26
  : null;
23
27
  const genericIngress = runtime
24
- ? resolveHostedEndpointPath(runtime.hby, pathname, "/", policy.hostedPrefixes)
28
+ ? resolveHostedEndpointPath(runtime.hby, pathname, "/", policy.hostedPrefixes, destination)
25
29
  : null;
26
30
  const oobiPath = hosted?.relativePath ?? pathname;
27
31
  return {
@@ -69,7 +69,7 @@ export function handleOobiRoute(context, request) {
69
69
  endpoint: null,
70
70
  relativePath: null,
71
71
  };
72
- const respondingHabAid = selectResponderHab(runtime, hosted, aid, request.eid, context.policy.hostedPrefixes);
72
+ const respondingHabAid = selectResponderHab(runtime, hosted, aid, request.eid, context.policy.hostedPrefixes, request.role);
73
73
  const hab = respondingHabAid ? runtime.hby.habs.get(respondingHabAid) : undefined;
74
74
  if (!hab) {
75
75
  if (hosted.kind === "ambiguous" && context.policy.hostedPrefixes) {
@@ -118,8 +118,12 @@ export function defaultOobiAid(runtime, serviceHab, hostedPrefixes) {
118
118
  * - the requested AID itself when locally controlled
119
119
  * - the explicit endpoint AID when it is locally controlled
120
120
  * - the hosted endpoint matched from the request path
121
+ * - a local identifier that an accepted end-role record of `aid` names as `eid` in the requested role, so a host
122
+ * serves the OOBI of an identifier it is the agent of even when it hosts no prefix directly. A non-empty
123
+ * `hostedPrefixes` is the operator's stated scope, so the named `eid` must be in it; when it is empty or absent,
124
+ * any local identifier qualifies.
121
125
  */
122
- export function selectResponderHab(runtime, hosted, aid, eid, hostedPrefixes) {
126
+ export function selectResponderHab(runtime, hosted, aid, eid, hostedPrefixes, role) {
123
127
  const hostedSet = hostedPrefixes ? new Set(hostedPrefixes) : null;
124
128
  const hostedCandidate = (candidate) => {
125
129
  if (!candidate || !runtime.hby.habs.has(candidate)) {
@@ -154,12 +158,13 @@ export function selectResponderHab(runtime, hosted, aid, eid, hostedPrefixes) {
154
158
  && (aid === hostedAid || eid === hostedAid)) {
155
159
  return hostedAid;
156
160
  }
157
- return undefined;
161
+ return endRoleResponder(runtime, aid, role, eid, hostedPrefixes);
158
162
  }
159
163
  return hostedCandidate(aid)
160
164
  ?? hostedCandidate(eid)
161
165
  ?? rootHostedCandidate(aid)
162
- ?? rootHostedCandidate(eid);
166
+ ?? rootHostedCandidate(eid)
167
+ ?? endRoleResponder(runtime, aid, role, eid, hostedPrefixes);
163
168
  }
164
169
  if (runtime.hby.habs.has(aid)) {
165
170
  return aid;
@@ -167,5 +172,19 @@ export function selectResponderHab(runtime, hosted, aid, eid, hostedPrefixes) {
167
172
  if (eid && runtime.hby.habs.has(eid)) {
168
173
  return eid;
169
174
  }
170
- return hosted.endpoint?.eid ?? undefined;
175
+ return hosted.endpoint?.eid ?? endRoleResponder(runtime, aid, role, eid, hostedPrefixes);
176
+ }
177
+ /**
178
+ * The local identifier an accepted, uncut end-role record of `aid` names as `eid` in `role` (any role when
179
+ * omitted; only `eid` when given), within `scope` when it is non-empty. Reads accepted state only: a role never accepted, or cut, names nobody.
180
+ */
181
+ function endRoleResponder(runtime, aid, role, eid, scope) {
182
+ for (const [keys, end] of runtime.hby.db.ends.getTopItemIter([aid], { topive: true })) {
183
+ const [, currentRole, agent] = keys;
184
+ if (!agent || !(end.allowed || end.enabled) || (role && currentRole !== role) || (eid && agent !== eid))
185
+ continue;
186
+ if (runtime.hby.habs.has(agent) && (!scope?.length || scope.includes(agent)))
187
+ return agent;
188
+ }
189
+ return undefined;
171
190
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keri-ts/hosting",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "HTTP host lifecycle, application middleware and protocol route composition",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,10 +32,10 @@
32
32
  "node": ">=20.3"
33
33
  },
34
34
  "dependencies": {
35
- "cesr-ts": "2.2.0",
35
+ "cesr-ts": "2.2.1",
36
36
  "effection": "^3.6.0",
37
37
  "hono": "^4.7.11",
38
- "keri-ts": "2.2.1",
38
+ "keri-ts": "2.2.2",
39
39
  "@deno/shim-deno": "~0.18.0"
40
40
  },
41
41
  "devDependencies": {
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * The router is path-first: every endpoint classifier receives the same
5
5
  * normalized path plus hosted-prefix projections, so mailbox admin, OOBI, and
6
- * generic CESR ingress cannot disagree about which local AID a URL addresses.
6
+ * generic CESR ingress cannot disagree about which local AID a URL addresses. When several local AIDs share
7
+ * one URL, the request's `CESR-DESTINATION` picks among them.
7
8
  */
8
9
  import { type AgentRuntime, type ProtocolHostPolicy } from "keri-ts/runtime";
9
10
  import type { ProtocolRequestContext } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/src/http/protocol/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAA6B,MAAM,iBAAiB,CAAC;AAExG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,gEAAgE;AAChE,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,OAAO,EACZ,OAAO,CAAC,EAAE,YAAY,EACtB,MAAM,GAAE,kBAAuB,GAC9B,sBAAsB,CA4CxB;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO9D"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/src/http/protocol/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,kBAAkB,EAExB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,gEAAgE;AAChE,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,OAAO,EACZ,OAAO,CAAC,EAAE,YAAY,EACtB,MAAM,GAAE,kBAAuB,GAC9B,sBAAsB,CAkDxB;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO9D"}
@@ -19,6 +19,10 @@ export declare function defaultOobiAid(runtime: AgentRuntime, serviceHab?: Hab,
19
19
  * - the requested AID itself when locally controlled
20
20
  * - the explicit endpoint AID when it is locally controlled
21
21
  * - the hosted endpoint matched from the request path
22
+ * - a local identifier that an accepted end-role record of `aid` names as `eid` in the requested role, so a host
23
+ * serves the OOBI of an identifier it is the agent of even when it hosts no prefix directly. A non-empty
24
+ * `hostedPrefixes` is the operator's stated scope, so the named `eid` must be in it; when it is empty or absent,
25
+ * any local identifier qualifies.
22
26
  */
23
27
  export declare function selectResponderHab(runtime: AgentRuntime, hosted: {
24
28
  kind: "none" | "one" | "ambiguous";
@@ -27,5 +31,5 @@ export declare function selectResponderHab(runtime: AgentRuntime, hosted: {
27
31
  url: string;
28
32
  basePath: string;
29
33
  } | null;
30
- }, aid: string, eid?: string, hostedPrefixes?: readonly string[]): string | undefined;
34
+ }, aid: string, eid?: string, hostedPrefixes?: readonly string[], role?: string): string | undefined;
31
35
  //# sourceMappingURL=oobi.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"oobi.d.ts","sourceRoot":"","sources":["../../../../../src/src/http/protocol/endpoints/oobi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAuC,KAAK,GAAG,EAAuB,MAAM,iBAAiB,CAAC;AAExH,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE3F,kEAAkE;AAClE,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GACf,gBAAgB,GAAG,IAAI,CAoCzB;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,sBAAsB,GAC9B,aAAa,GAAG,IAAI,CAWtB;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,EAC/B,OAAO,EAAE,gBAAgB,GACxB,QAAQ,CA0DV;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,UAAU,CAAC,EAAE,GAAG,EAChB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,GACjC,MAAM,GAAG,SAAS,CAcpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE;IACN,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,WAAW,CAAC;IACnC,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE,EACD,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,EACZ,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,GACjC,MAAM,GAAG,SAAS,CAqDpB"}
1
+ {"version":3,"file":"oobi.d.ts","sourceRoot":"","sources":["../../../../../src/src/http/protocol/endpoints/oobi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAuC,KAAK,GAAG,EAAuB,MAAM,iBAAiB,CAAC;AAExH,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE3F,kEAAkE;AAClE,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GACf,gBAAgB,GAAG,IAAI,CAoCzB;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,sBAAsB,GAC9B,aAAa,GAAG,IAAI,CAWtB;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,EAC/B,OAAO,EAAE,gBAAgB,GACxB,QAAQ,CA2DV;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,YAAY,EACrB,UAAU,CAAC,EAAE,GAAG,EAChB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,GACjC,MAAM,GAAG,SAAS,CAcpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE;IACN,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,WAAW,CAAC;IACnC,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjE,EACD,GAAG,EAAE,MAAM,EACX,GAAG,CAAC,EAAE,MAAM,EACZ,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,EAClC,IAAI,CAAC,EAAE,MAAM,GACZ,MAAM,GAAG,SAAS,CAsDpB"}