keri-ts 2.2.4 → 2.2.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.
- package/esm/keri/npm/src/_dnt.polyfills.js +14 -14
- package/esm/keri/npm/src/acdc/dhking.js +29 -2
- package/esm/keri/npm/src/acdc/observing.js +10 -3
- package/esm/keri/npm/src/acdc/query-resend.js +74 -0
- package/esm/keri/npm/src/acdc/regengine.js +39 -9
- package/esm/keri/npm/src/acdc/regeventing.js +5 -3
- package/esm/keri/npm/src/acdc/registering.js +19 -0
- package/esm/keri/npm/src/acdc/salting.js +4 -0
- package/esm/keri/npm/src/acdc/unblinding.js +38 -3
- package/esm/keri/npm/src/app/bootstrap/authenticating.js +32 -10
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +363 -30
- package/esm/keri/npm/src/app/credentials/credential-importing.js +33 -4
- package/esm/keri/npm/src/app/credentials/root-replicator.js +27 -15
- package/esm/keri/npm/src/app/credentials/schema-resolving.js +5 -1
- package/esm/keri/npm/src/app/credentials/tel-publishing.js +16 -7
- package/esm/keri/npm/src/app/delivery/forwarding.js +205 -44
- package/esm/keri/npm/src/app/delivery/relaying.js +232 -0
- package/esm/keri/npm/src/app/delivery/respondant.js +8 -5
- package/esm/keri/npm/src/app/exchanges/exchange-messaging.js +1 -1
- package/esm/keri/npm/src/app/exchanges/exchanging.js +65 -9
- package/esm/keri/npm/src/app/identity/delegating.js +66 -9
- package/esm/keri/npm/src/app/identity/habbing.js +190 -71
- package/esm/keri/npm/src/app/identity/keeping.js +111 -39
- package/esm/keri/npm/src/app/identity/retire-identifier.js +98 -0
- package/esm/keri/npm/src/app/ipex/disclosure-replaying.js +116 -0
- package/esm/keri/npm/src/app/ipex/group-ipex.js +122 -25
- package/esm/keri/npm/src/app/ipex/group-issuance.js +137 -37
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +94 -31
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +53 -3
- package/esm/keri/npm/src/app/ipex/ipex-v2.js +7 -2
- package/esm/keri/npm/src/app/ipex/issuance.js +147 -44
- package/esm/keri/npm/src/app/ipex/pending-presentations.js +4 -2
- package/esm/keri/npm/src/app/ipex/presenting.js +3 -18
- package/esm/keri/npm/src/app/ipex/receive-issuance.js +2 -1
- package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +39 -11
- package/esm/keri/npm/src/app/mailbox/agent-inbox.js +292 -0
- package/esm/keri/npm/src/app/mailbox/forward-handler.js +298 -52
- package/esm/keri/npm/src/app/mailbox/kel-publisher.js +26 -31
- package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +70 -62
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +20 -4
- package/esm/keri/npm/src/app/multisig/counseling.js +71 -27
- package/esm/keri/npm/src/app/multisig/group-members.js +41 -5
- package/esm/keri/npm/src/app/multisig/grouping.js +50 -2
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +3 -18
- package/esm/keri/npm/src/app/multisig/proposing.js +1 -16
- package/esm/keri/npm/src/app/multisig/rejoin-group.js +99 -0
- package/esm/keri/npm/src/app/multisig/replace-member.js +29 -8
- package/esm/keri/npm/src/app/protocol/parsering.js +45 -17
- package/esm/keri/npm/src/app/protocol/protocol-processing.js +47 -21
- package/esm/keri/npm/src/app/queries/query-replies.js +0 -22
- package/esm/keri/npm/src/app/queries/relay-refresh.js +81 -0
- package/esm/keri/npm/src/app/queries/witness-inquisitor.js +180 -34
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +42 -73
- package/esm/keri/npm/src/app/runtime/cue-runtime.js +45 -5
- package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +8 -1
- package/esm/keri/npm/src/app/runtime/operation-owners.js +26 -56
- package/esm/keri/npm/src/app/runtime/workflow-contract.js +7 -4
- package/esm/keri/npm/src/app/transport/http-client.js +20 -2
- package/esm/keri/npm/src/app/transport/message-source.js +2 -1
- package/esm/keri/npm/src/app/transport/messengering.js +39 -8
- package/esm/keri/npm/src/app/witness/ingress.js +7 -0
- package/esm/keri/npm/src/app/witness/receipt-client.js +43 -16
- package/esm/keri/npm/src/app/witness/receipting.js +62 -20
- package/esm/keri/npm/src/cli/messaging/multisig.js +4 -3
- package/esm/keri/npm/src/core/errors.js +73 -0
- package/esm/keri/npm/src/core/kel/delegation-ancestry.js +11 -2
- package/esm/keri/npm/src/core/kel/eventing.js +25 -28
- package/esm/keri/npm/src/core/kel/kever.js +15 -9
- package/esm/keri/npm/src/core/mailbox/kram-policy.js +16 -0
- package/esm/keri/npm/src/core/mailbox/kram-pruning.js +25 -6
- package/esm/keri/npm/src/core/mailbox/kram-time.js +34 -6
- package/esm/keri/npm/src/core/mailbox/kraming.js +58 -2
- package/esm/keri/npm/src/core/missing-anchor-query.js +20 -0
- package/esm/keri/npm/src/core/routing.js +18 -25
- package/esm/keri/npm/src/core/scheming.js +12 -5
- package/esm/keri/npm/src/core/wire-fields.js +126 -0
- package/esm/keri/npm/src/db/basing.js +4 -13
- package/esm/keri/npm/src/db/core/indexeddb-persistence.js +3 -2
- package/esm/keri/npm/src/db/core/webdber.js +85 -27
- package/esm/keri/npm/src/db/mailbox/mailboxing.js +13 -0
- package/esm/keri/npm/src/db/subing.js +4 -0
- package/esm/keri/npm/src/runtime/index.js +11 -5
- package/esm/keri/npm/src/time/mod.js +5 -0
- package/esm/keri/npm/src/vdr/credential-tsn-record.js +2 -7
- package/esm/keri/npm/src/vdr/eventing.js +71 -28
- package/esm/keri/npm/src/vdr/registry-tsn-record.js +3 -13
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +1 -1
- package/types/_dnt.polyfills.d.ts +9 -9
- package/types/_dnt.polyfills.d.ts.map +1 -1
- package/types/acdc/dhking.d.ts +8 -0
- package/types/acdc/dhking.d.ts.map +1 -1
- package/types/acdc/observing.d.ts +5 -1
- package/types/acdc/observing.d.ts.map +1 -1
- package/types/acdc/query-resend.d.ts +24 -0
- package/types/acdc/query-resend.d.ts.map +1 -0
- package/types/acdc/regengine.d.ts +19 -1
- package/types/acdc/regengine.d.ts.map +1 -1
- package/types/acdc/regeventing.d.ts.map +1 -1
- package/types/acdc/registering.d.ts +6 -0
- package/types/acdc/registering.d.ts.map +1 -1
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts +13 -1
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/bootstrap/authenticating.d.ts +12 -2
- package/types/app/bootstrap/authenticating.d.ts.map +1 -1
- package/types/app/bootstrap/oobiery.d.ts +144 -7
- package/types/app/bootstrap/oobiery.d.ts.map +1 -1
- package/types/app/credentials/credential-importing.d.ts +8 -0
- package/types/app/credentials/credential-importing.d.ts.map +1 -1
- package/types/app/credentials/root-replicator.d.ts +19 -8
- package/types/app/credentials/root-replicator.d.ts.map +1 -1
- package/types/app/credentials/schema-resolving.d.ts.map +1 -1
- package/types/app/credentials/tel-publishing.d.ts +4 -2
- package/types/app/credentials/tel-publishing.d.ts.map +1 -1
- package/types/app/delivery/forwarding.d.ts +90 -5
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/delivery/relaying.d.ts +144 -0
- package/types/app/delivery/relaying.d.ts.map +1 -0
- package/types/app/delivery/respondant.d.ts +9 -2
- package/types/app/delivery/respondant.d.ts.map +1 -1
- package/types/app/exchanges/exchanging.d.ts +36 -0
- package/types/app/exchanges/exchanging.d.ts.map +1 -1
- package/types/app/identity/delegating.d.ts +6 -0
- package/types/app/identity/delegating.d.ts.map +1 -1
- package/types/app/identity/habbing.d.ts +106 -19
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +54 -19
- package/types/app/identity/keeping.d.ts.map +1 -1
- package/types/app/identity/retire-identifier.d.ts +24 -0
- package/types/app/identity/retire-identifier.d.ts.map +1 -0
- package/types/app/ipex/disclosure-replaying.d.ts +61 -0
- package/types/app/ipex/disclosure-replaying.d.ts.map +1 -0
- package/types/app/ipex/group-ipex.d.ts +60 -14
- package/types/app/ipex/group-ipex.d.ts.map +1 -1
- package/types/app/ipex/group-issuance.d.ts +54 -8
- package/types/app/ipex/group-issuance.d.ts.map +1 -1
- package/types/app/ipex/ipex-discloser.d.ts +39 -2
- package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
- package/types/app/ipex/ipex-grouping.d.ts +23 -2
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/ipex-v2.d.ts +5 -2
- package/types/app/ipex/ipex-v2.d.ts.map +1 -1
- package/types/app/ipex/issuance.d.ts +46 -16
- package/types/app/ipex/issuance.d.ts.map +1 -1
- package/types/app/ipex/pending-presentations.d.ts +4 -2
- package/types/app/ipex/pending-presentations.d.ts.map +1 -1
- package/types/app/ipex/presenting.d.ts.map +1 -1
- package/types/app/ipex/receive-issuance.d.ts +2 -1
- package/types/app/ipex/receive-issuance.d.ts.map +1 -1
- package/types/app/ipex/unblinding-workflow.d.ts +13 -2
- package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
- package/types/app/mailbox/agent-inbox.d.ts +126 -0
- package/types/app/mailbox/agent-inbox.d.ts.map +1 -0
- package/types/app/mailbox/forward-handler.d.ts +114 -14
- package/types/app/mailbox/forward-handler.d.ts.map +1 -1
- package/types/app/mailbox/kel-publisher.d.ts +12 -22
- package/types/app/mailbox/kel-publisher.d.ts.map +1 -1
- package/types/app/mailbox/mailbox-poller.d.ts +40 -24
- package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +19 -5
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/counseling.d.ts +32 -11
- package/types/app/multisig/counseling.d.ts.map +1 -1
- package/types/app/multisig/group-members.d.ts +17 -1
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts +17 -1
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts +0 -8
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/multisig/proposing.d.ts +4 -19
- package/types/app/multisig/proposing.d.ts.map +1 -1
- package/types/app/multisig/rejoin-group.d.ts +119 -0
- package/types/app/multisig/rejoin-group.d.ts.map +1 -0
- package/types/app/multisig/replace-member.d.ts +15 -1
- package/types/app/multisig/replace-member.d.ts.map +1 -1
- package/types/app/protocol/parsering.d.ts +21 -2
- package/types/app/protocol/parsering.d.ts.map +1 -1
- package/types/app/protocol/protocol-processing.d.ts +25 -5
- package/types/app/protocol/protocol-processing.d.ts.map +1 -1
- package/types/app/queries/query-replies.d.ts +4 -8
- package/types/app/queries/query-replies.d.ts.map +1 -1
- package/types/app/queries/relay-refresh.d.ts +26 -0
- package/types/app/queries/relay-refresh.d.ts.map +1 -0
- package/types/app/queries/witness-inquisitor.d.ts +56 -5
- package/types/app/queries/witness-inquisitor.d.ts.map +1 -1
- package/types/app/runtime/agent-runtime.d.ts +34 -3
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/runtime/cue-runtime.d.ts +15 -3
- package/types/app/runtime/cue-runtime.d.ts.map +1 -1
- package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -1
- package/types/app/runtime/operation-owners.d.ts +16 -26
- package/types/app/runtime/operation-owners.d.ts.map +1 -1
- package/types/app/runtime/workflow-contract.d.ts +21 -6
- package/types/app/runtime/workflow-contract.d.ts.map +1 -1
- package/types/app/transport/http-client.d.ts +9 -0
- package/types/app/transport/http-client.d.ts.map +1 -1
- package/types/app/transport/message-source.d.ts +6 -3
- package/types/app/transport/message-source.d.ts.map +1 -1
- package/types/app/transport/messengering.d.ts +10 -1
- package/types/app/transport/messengering.d.ts.map +1 -1
- package/types/app/witness/ingress.d.ts +1 -0
- package/types/app/witness/ingress.d.ts.map +1 -1
- package/types/app/witness/receipt-client.d.ts +19 -1
- package/types/app/witness/receipt-client.d.ts.map +1 -1
- package/types/app/witness/receipting.d.ts +24 -1
- package/types/app/witness/receipting.d.ts.map +1 -1
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/core/cues.d.ts +53 -1
- package/types/core/cues.d.ts.map +1 -1
- package/types/core/errors.d.ts +32 -0
- package/types/core/errors.d.ts.map +1 -1
- package/types/core/kel/delegation-ancestry.d.ts.map +1 -1
- package/types/core/kel/eventing.d.ts.map +1 -1
- package/types/core/kel/kever-decisions.d.ts +3 -1
- package/types/core/kel/kever-decisions.d.ts.map +1 -1
- package/types/core/kel/kever.d.ts +7 -1
- package/types/core/kel/kever.d.ts.map +1 -1
- package/types/core/mailbox/kram-policy.d.ts +12 -0
- package/types/core/mailbox/kram-policy.d.ts.map +1 -1
- package/types/core/mailbox/kram-pruning.d.ts +5 -1
- package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
- package/types/core/mailbox/kram-time.d.ts +26 -2
- package/types/core/mailbox/kram-time.d.ts.map +1 -1
- package/types/core/mailbox/kraming.d.ts +14 -1
- package/types/core/mailbox/kraming.d.ts.map +1 -1
- package/types/core/missing-anchor-query.d.ts +34 -0
- package/types/core/missing-anchor-query.d.ts.map +1 -0
- package/types/core/routing.d.ts +3 -7
- package/types/core/routing.d.ts.map +1 -1
- package/types/core/scheming.d.ts.map +1 -1
- package/types/core/wire-fields.d.ts +56 -0
- package/types/core/wire-fields.d.ts.map +1 -0
- package/types/db/basing.d.ts.map +1 -1
- package/types/db/core/indexeddb-persistence.d.ts +3 -2
- package/types/db/core/indexeddb-persistence.d.ts.map +1 -1
- package/types/db/core/webdber.d.ts +32 -6
- package/types/db/core/webdber.d.ts.map +1 -1
- package/types/db/exchange/exchange-records.d.ts +4 -0
- package/types/db/exchange/exchange-records.d.ts.map +1 -1
- package/types/db/mailbox/mailboxing.d.ts +10 -1
- package/types/db/mailbox/mailboxing.d.ts.map +1 -1
- package/types/db/subing.d.ts +2 -0
- package/types/db/subing.d.ts.map +1 -1
- package/types/runtime/index.d.ts +20 -14
- package/types/runtime/index.d.ts.map +1 -1
- package/types/time/mod.d.ts +2 -0
- package/types/time/mod.d.ts.map +1 -1
- package/types/vdr/credential-tsn-record.d.ts.map +1 -1
- package/types/vdr/eventing.d.ts +32 -1
- package/types/vdr/eventing.d.ts.map +1 -1
- package/types/vdr/registry-tsn-record.d.ts.map +1 -1
- package/types/version.d.ts +2 -2
- package/esm/keri/npm/src/app/runtime/signer-conditions.js +0 -51
- package/types/app/runtime/signer-conditions.d.ts +0 -32
- package/types/app/runtime/signer-conditions.d.ts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ilks, Prefixer, SerderKERI } from "cesr-ts";
|
|
1
|
+
import { Ilks, ParserError, Prefixer, SerderKERI, WireParseError } from "cesr-ts";
|
|
2
2
|
import { call, resource, scoped } from "effection";
|
|
3
3
|
import { ValidationError } from "../../core/errors.js";
|
|
4
4
|
import { exchange } from "../../core/exchange/protocol-exchanging.js";
|
|
@@ -6,13 +6,31 @@ import { OOBI_MAILBOX_TOPIC } from "../../core/mailbox/mailbox-topics.js";
|
|
|
6
6
|
import { Roles } from "../../core/roles.js";
|
|
7
7
|
import { acceptReplyDecision, unverifiedReplyDecision } from "../../core/routing.js";
|
|
8
8
|
import { Schemer } from "../../core/scheming.js";
|
|
9
|
+
import { isRelayOrigin, relayAuthorized, relayEndpoint, RelayOnlyError, relayRefusal, } from "../delivery/relaying.js";
|
|
9
10
|
import { persistResolvedContact } from "../identity/organizing.js";
|
|
11
|
+
import { servingHost } from "../mailbox/forward-handler.js";
|
|
10
12
|
import { abortable } from "../runtime/abortable.js";
|
|
11
13
|
import { doEveryOnWrite, PROTOCOL_PACING } from "../runtime/doing.js";
|
|
12
14
|
import { systemClock } from "../runtime/runtime-clock.js";
|
|
13
15
|
import { collectBytes } from "../transport/byte-source.js";
|
|
16
|
+
import { isRequestableHttpUrl } from "../transport/http-client.js";
|
|
14
17
|
import { processMessages } from "../transport/message-source.js";
|
|
18
|
+
import { TransportError } from "../transport/transport-error.js";
|
|
15
19
|
export const OOBI_REQUEST_ROUTE = "/oobis";
|
|
20
|
+
/**
|
|
21
|
+
* How long an edge's OOBI job waits for its relay to hold the answer before it fails: 30 seconds, measured from the
|
|
22
|
+
* job's first relay attempt on the composed clock. It matches KERIpy's `Oobiery.RetryDelay` and the default
|
|
23
|
+
* `bootstrapOobis` timeout; the keri-ts Oobiery has no other retry, and a direct fetch fails on its first non-OK
|
|
24
|
+
* answer.
|
|
25
|
+
*/
|
|
26
|
+
export const RELAY_OOBI_TIMEOUT_MS = 30_000;
|
|
27
|
+
/** Job states of an OOBI an edge resolves through its relay; any other state starts a new relay wait. */
|
|
28
|
+
const RelayOobiStates = Object.freeze({
|
|
29
|
+
/** Waiting; the relay has not been asked yet (its location or a serving authorization is missing). */
|
|
30
|
+
pending: "relay-pending",
|
|
31
|
+
/** Waiting; the relay was asked with `/oobis` and has not answered with the result yet. */
|
|
32
|
+
requested: "relay-requested",
|
|
33
|
+
});
|
|
16
34
|
/**
|
|
17
35
|
* Return the current terminal state projection for one requested OOBI URL.
|
|
18
36
|
*
|
|
@@ -49,6 +67,29 @@ export function oobiTerminalState(db, url, kind = queueKindFor(url)) {
|
|
|
49
67
|
}
|
|
50
68
|
return { status: "pending", via: "none", record: null };
|
|
51
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Classify an error that ended one OOBI or authentication job: the {@link OobiJobFailure} a remote party can cause,
|
|
72
|
+
* or null for a fault that must propagate and end the runtime (a store error such as `DatabaseError`, corrupt
|
|
73
|
+
* durable state, a programmer error such as `TypeError`, a plain `Error`).
|
|
74
|
+
*
|
|
75
|
+
* Limits: a cesr `ParserError`, `SyntaxError` or `RangeError` raised while decoding local stored state during the
|
|
76
|
+
* same dispatch is indistinguishable by class from one raised by a remote field, so it also ends only that job.
|
|
77
|
+
*
|
|
78
|
+
* @param error What escaped the job's request or response ingestion.
|
|
79
|
+
* @returns The failure to record, or null to rethrow.
|
|
80
|
+
*/
|
|
81
|
+
export function oobiJobFailure(error) {
|
|
82
|
+
if (error instanceof RelayOnlyError)
|
|
83
|
+
return "egress-refused";
|
|
84
|
+
if (error instanceof TransportError)
|
|
85
|
+
return "transport-failed";
|
|
86
|
+
if (error instanceof WireParseError)
|
|
87
|
+
return "unparseable-response";
|
|
88
|
+
if (error instanceof ParserError || error instanceof SyntaxError || error instanceof RangeError) {
|
|
89
|
+
return "invalid-response-frame";
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
52
93
|
/**
|
|
53
94
|
* Durable OOBI processing component for one `Habery`.
|
|
54
95
|
*
|
|
@@ -71,10 +112,11 @@ export class Oobiery {
|
|
|
71
112
|
* @param rvy Actual shared reply verifier used for OOBI reply proof persistence.
|
|
72
113
|
* @param http Borrowed HTTP implementation selected by composition.
|
|
73
114
|
* @param cues Required application result Deck supplied by composition.
|
|
115
|
+
* @param relaying Relay collaborators of an edge runtime; null (a forwarder) resolves every OOBI directly.
|
|
74
116
|
*/
|
|
75
117
|
constructor(hby, protocol,
|
|
76
118
|
/** Borrowed reply verifier; route installation and proof writes share this owner. */
|
|
77
|
-
rvy, http, cues) {
|
|
119
|
+
rvy, http, cues, relaying = null) {
|
|
78
120
|
Object.defineProperty(this, "protocol", {
|
|
79
121
|
enumerable: true,
|
|
80
122
|
configurable: true,
|
|
@@ -115,6 +157,13 @@ export class Oobiery {
|
|
|
115
157
|
writable: true,
|
|
116
158
|
value: void 0
|
|
117
159
|
});
|
|
160
|
+
/** Relay collaborators of an edge; null resolves every OOBI directly. */
|
|
161
|
+
Object.defineProperty(this, "relaying", {
|
|
162
|
+
enumerable: true,
|
|
163
|
+
configurable: true,
|
|
164
|
+
writable: true,
|
|
165
|
+
value: void 0
|
|
166
|
+
});
|
|
118
167
|
/** Prevent overlapping passes over this owner's durable job queue. */
|
|
119
168
|
Object.defineProperty(this, "active", {
|
|
120
169
|
enumerable: true,
|
|
@@ -122,6 +171,16 @@ export class Oobiery {
|
|
|
122
171
|
writable: true,
|
|
123
172
|
value: false
|
|
124
173
|
});
|
|
174
|
+
/**
|
|
175
|
+
* URL of the last job handled with a relay, so a job left waiting does not starve the jobs after it: the next
|
|
176
|
+
* pass takes the first queued URL after this one. Always null without a relay.
|
|
177
|
+
*/
|
|
178
|
+
Object.defineProperty(this, "relayCursor", {
|
|
179
|
+
enumerable: true,
|
|
180
|
+
configurable: true,
|
|
181
|
+
writable: true,
|
|
182
|
+
value: null
|
|
183
|
+
});
|
|
125
184
|
this.hby = hby;
|
|
126
185
|
this.kvy = protocol.makeKevery();
|
|
127
186
|
if (this.kvy.db !== hby.db || this.kvy.rvy !== rvy) {
|
|
@@ -129,6 +188,7 @@ export class Oobiery {
|
|
|
129
188
|
}
|
|
130
189
|
this.cues = cues;
|
|
131
190
|
this.http = http;
|
|
191
|
+
this.relaying = relaying;
|
|
132
192
|
}
|
|
133
193
|
/**
|
|
134
194
|
* Register `/introduce` on the shared reply router.
|
|
@@ -209,16 +269,24 @@ export class Oobiery {
|
|
|
209
269
|
*oobiDo(clock = systemClock, pacing = PROTOCOL_PACING) {
|
|
210
270
|
return yield* doEveryOnWrite(clock, pacing, this.hby.db, () => this.processOnce());
|
|
211
271
|
}
|
|
212
|
-
/**
|
|
272
|
+
/**
|
|
273
|
+
* Select queued work, including a surviving in-flight row whose former process is gone. After a relay job, the
|
|
274
|
+
* first queued URL after it is taken, wrapping around, so jobs waiting on the relay take turns.
|
|
275
|
+
*/
|
|
213
276
|
nextQueued() {
|
|
277
|
+
const after = this.relayCursor;
|
|
278
|
+
let first = null;
|
|
214
279
|
for (const store of [this.hby.db.oobis, this.hby.db.coobi]) {
|
|
215
280
|
for (const [keys, record] of store.getTopItemIter()) {
|
|
216
281
|
const url = keys[0];
|
|
217
|
-
if (url)
|
|
282
|
+
if (!url)
|
|
283
|
+
continue;
|
|
284
|
+
if (after === null || url > after)
|
|
218
285
|
return ["oobis", url, record];
|
|
286
|
+
first ??= ["oobis", url, record];
|
|
219
287
|
}
|
|
220
288
|
}
|
|
221
|
-
return
|
|
289
|
+
return first;
|
|
222
290
|
}
|
|
223
291
|
/** Select a parent only after its current child attempts, including authentication, settle. */
|
|
224
292
|
nextReadyMoobi() {
|
|
@@ -238,7 +306,7 @@ export class Oobiery {
|
|
|
238
306
|
* Stores touched:
|
|
239
307
|
* - `oobis.` / `woobi.` as the durable queued worklists
|
|
240
308
|
* - `coobi.` while a fetched response is being processed
|
|
241
|
-
* - `eoobi.` on HTTP failure
|
|
309
|
+
* - `eoobi.` on HTTP failure or an {@link OobiJobFailure}; the queued row is removed so it is not re-read
|
|
242
310
|
* - `roobi.` after parser/routing success
|
|
243
311
|
*
|
|
244
312
|
* @param kind Durable OOBI queue family identifying the job path.
|
|
@@ -246,6 +314,17 @@ export class Oobiery {
|
|
|
246
314
|
* @param record Pending durable job; retained through failure and joined cleanup.
|
|
247
315
|
*/
|
|
248
316
|
*processJob(kind, url, record) {
|
|
317
|
+
if (!isRequestableHttpUrl(url)) {
|
|
318
|
+
// Neither this host nor a relay can fetch it; fail it before any request instead of on every pass.
|
|
319
|
+
this.remQueueStore(kind, url);
|
|
320
|
+
this.failFetchedOobi(url, record, "url-refused");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (this.relaying && !isRelayOrigin(this.hby, this.relaying.relay, url)) {
|
|
324
|
+
this.relayCursor = url;
|
|
325
|
+
yield* this.processRelayJob(this.relaying, kind, url, record);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
249
328
|
const http = this.http;
|
|
250
329
|
const meta = {
|
|
251
330
|
...parseOobiUrl(url, record.oobialias ?? undefined),
|
|
@@ -288,6 +367,14 @@ export class Oobiery {
|
|
|
288
367
|
this.cues.push({ kin: "oobiFailed", url, reason: `HTTP ${refused}` });
|
|
289
368
|
}
|
|
290
369
|
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
// What the remote party caused ends this job only; the response scope has closed.
|
|
372
|
+
const failure = oobiJobFailure(error);
|
|
373
|
+
if (failure === null)
|
|
374
|
+
throw error;
|
|
375
|
+
this.remQueueStore(kind, url);
|
|
376
|
+
this.failFetchedOobi(url, queuedRecord, failure);
|
|
377
|
+
}
|
|
291
378
|
finally {
|
|
292
379
|
this.hby.db.coobi.rem(url);
|
|
293
380
|
}
|
|
@@ -349,13 +436,186 @@ export class Oobiery {
|
|
|
349
436
|
role: meta.role ?? undefined,
|
|
350
437
|
eid: meta.eid ?? undefined,
|
|
351
438
|
};
|
|
439
|
+
yield* this.ingestCesrOobiResponse(kind, url, queuedRecord, response);
|
|
440
|
+
this.hby.db.roobi.pin(url, { ...queuedRecord, date: new Date().toISOString(), state: "resolved" });
|
|
441
|
+
persistResolvedContact(this.hby, meta.cid, { alias: queuedRecord.oobialias, oobi: url });
|
|
442
|
+
this.cues.push(cue);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Dispatch one complete CESR OOBI response through this owner's Kevery and the shared semantic handlers, keeping
|
|
446
|
+
* the queued row and an in-flight `coobi.` row until the stream is consumed. Writes no terminal state.
|
|
447
|
+
*
|
|
448
|
+
* @param kind Durable OOBI queue family identifying the job path.
|
|
449
|
+
* @param url Exact OOBI URL used as the durable job key.
|
|
450
|
+
* @param queuedRecord Original durable job metadata retained until terminal publication.
|
|
451
|
+
* @param response Borrowed scoped HTTP response.
|
|
452
|
+
*/
|
|
453
|
+
*ingestCesrOobiResponse(kind, url, queuedRecord, response) {
|
|
352
454
|
this.pinQueueStore(kind, url, queuedRecord);
|
|
353
455
|
this.hby.db.coobi.pin(url, { ...queuedRecord, state: "streaming" });
|
|
354
456
|
yield* processMessages((signal) => response.body(signal), this.protocol, this.kvy);
|
|
355
457
|
this.hby.db.coobi.rem(url);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Resolve one job through the relay: ask it once, then fetch the result from the relay's own OOBI endpoint.
|
|
461
|
+
*
|
|
462
|
+
* The job stays in `oobis.` while it waits, dated from its first relay attempt; a pass that changes nothing
|
|
463
|
+
* writes nothing. It waits while the relay answers 404, is unreachable, or answers a controller OOBI without the
|
|
464
|
+
* controller's KEL. It fails at {@link RELAY_OOBI_TIMEOUT_MS}: `relay-unresolved` when the relay was asked,
|
|
465
|
+
* `relay-unserved` when it never could be (no accepted relay location, or no local identifier authorized the
|
|
466
|
+
* relay as its mailbox). Another non-OK answer fails at once as `http-<status>`, as a direct fetch does, an answer
|
|
467
|
+
* that is not CESR or whose frames do not decode fails at once with its {@link OobiJobFailure}, and a URL that names
|
|
468
|
+
* neither an identifier nor a SAID fails at once as `relay-unmappable-oobi`.
|
|
469
|
+
*
|
|
470
|
+
* @param relaying Relay collaborators of this edge.
|
|
471
|
+
* @param kind Durable OOBI queue family identifying the job path.
|
|
472
|
+
* @param url Exact third-party OOBI URL used as the durable job key; it is never fetched.
|
|
473
|
+
* @param record Pending durable job.
|
|
474
|
+
*/
|
|
475
|
+
*processRelayJob(relaying, kind, url, record) {
|
|
476
|
+
const { relay, postman, clock } = relaying;
|
|
477
|
+
const meta = { ...parseOobiUrl(url, record.oobialias ?? undefined), ...record, url };
|
|
478
|
+
const path = relayOobiPath(meta);
|
|
479
|
+
if (path === null) {
|
|
480
|
+
this.remQueueStore(kind, url);
|
|
481
|
+
this.failFetchedOobi(url, record, "relay-unmappable-oobi");
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
let waiting = record;
|
|
485
|
+
if (record.state !== RelayOobiStates.pending && record.state !== RelayOobiStates.requested) {
|
|
486
|
+
waiting = {
|
|
487
|
+
...record,
|
|
488
|
+
date: new Date(clock.now()).toISOString(),
|
|
489
|
+
state: RelayOobiStates.pending,
|
|
490
|
+
cid: meta.cid ?? null,
|
|
491
|
+
eid: meta.eid ?? null,
|
|
492
|
+
role: meta.role ?? null,
|
|
493
|
+
oobialias: meta.alias ?? meta.oobialias ?? null,
|
|
494
|
+
said: meta.said ?? null,
|
|
495
|
+
};
|
|
496
|
+
this.hby.db.transaction(() => {
|
|
497
|
+
this.pinQueueStore(kind, url, waiting);
|
|
498
|
+
this.hby.db.eoobi.rem(url);
|
|
499
|
+
this.hby.db.roobi.rem(url);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
if (waiting.state === RelayOobiStates.pending) {
|
|
503
|
+
const asker = this.relayAsker(relay);
|
|
504
|
+
if (asker) {
|
|
505
|
+
const exn = oobiRequestExn(asker, relay.aid, url);
|
|
506
|
+
postman.send({
|
|
507
|
+
src: asker.pre,
|
|
508
|
+
dest: relay.aid,
|
|
509
|
+
topic: OOBI_MAILBOX_TOPIC,
|
|
510
|
+
msg: asker.endorse(exn, { pipelined: false }),
|
|
511
|
+
});
|
|
512
|
+
waiting = { ...waiting, state: RelayOobiStates.requested };
|
|
513
|
+
this.pinQueueStore(kind, url, waiting);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
const endpoint = relayEndpoint(this.hby, relay);
|
|
517
|
+
const base = endpoint?.urls.https ?? endpoint?.urls.http;
|
|
518
|
+
if (base) {
|
|
519
|
+
const refused = yield* this.fetchFromRelay(kind, url, waiting, meta, new URL(path, withSlash(base)).href);
|
|
520
|
+
if (refused === "resolved")
|
|
521
|
+
return;
|
|
522
|
+
if (typeof refused === "number") {
|
|
523
|
+
this.remQueueStore(kind, url);
|
|
524
|
+
this.hby.db.eoobi.pin(url, { ...waiting, date: new Date().toISOString(), state: `http-${refused}` });
|
|
525
|
+
this.cues.push({ kin: "oobiFailed", url, reason: `HTTP ${refused}` });
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (refused !== "waiting") {
|
|
529
|
+
this.remQueueStore(kind, url);
|
|
530
|
+
this.failFetchedOobi(url, waiting, refused);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (clock.now() - Date.parse(waiting.date ?? "") < RELAY_OOBI_TIMEOUT_MS)
|
|
535
|
+
return;
|
|
536
|
+
const state = waiting.state === RelayOobiStates.requested ? "relay-unresolved" : "relay-unserved";
|
|
537
|
+
this.remQueueStore(kind, url);
|
|
538
|
+
this.failFetchedOobi(url, waiting, state);
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Fetch what the relay holds for one job and ingest it under the ordinary verification.
|
|
542
|
+
*
|
|
543
|
+
* @param kind Durable OOBI queue family identifying the job path.
|
|
544
|
+
* @param url Exact third-party OOBI URL used as the durable job key.
|
|
545
|
+
* @param record Waiting durable job metadata.
|
|
546
|
+
* @param meta Parsed OOBI URL metadata selecting the response interpretation.
|
|
547
|
+
* @param relayUrl URL on the relay's origin that serves the job's answer.
|
|
548
|
+
* @returns `resolved` after a terminal write, `waiting` when the relay does not hold the answer yet or cannot be
|
|
549
|
+
* reached, the refusing HTTP status, or the {@link OobiJobFailure} of an answer that cannot be ingested.
|
|
550
|
+
*/
|
|
551
|
+
*fetchFromRelay(kind, url, record, meta, relayUrl) {
|
|
552
|
+
const http = this.http;
|
|
553
|
+
// deno-lint-ignore no-this-alias -- The scoped body borrows this OOBI worker's durable queue.
|
|
554
|
+
const self = this;
|
|
555
|
+
try {
|
|
556
|
+
return yield* scoped(function* () {
|
|
557
|
+
const response = yield* http.open(relayUrl);
|
|
558
|
+
if (response.status === 404)
|
|
559
|
+
return "waiting";
|
|
560
|
+
if (!response.ok)
|
|
561
|
+
return response.status;
|
|
562
|
+
const type = mediaType(response.headers.get("content-type") ?? "");
|
|
563
|
+
if (!isCesrMediaType(type) && type.includes("json")) {
|
|
564
|
+
// Schema and JSON OOBI bodies reach a terminal state under the direct-fetch rules.
|
|
565
|
+
yield* self.processResponse(kind, url, record, meta, response);
|
|
566
|
+
self.remQueueStore(kind, url);
|
|
567
|
+
return "resolved";
|
|
568
|
+
}
|
|
569
|
+
yield* self.ingestCesrOobiResponse(kind, url, record, response);
|
|
570
|
+
// An answer without the controller's KEL is not the result yet; the relay may still be resolving it.
|
|
571
|
+
if (meta.cid && !self.hby.db.getKever(meta.cid))
|
|
572
|
+
return "waiting";
|
|
573
|
+
self.remQueueStore(kind, url);
|
|
574
|
+
self.hby.db.roobi.pin(url, { ...record, date: new Date().toISOString(), state: "resolved" });
|
|
575
|
+
persistResolvedContact(self.hby, meta.cid, { alias: record.oobialias, oobi: url });
|
|
576
|
+
self.cues.push({
|
|
577
|
+
kin: "oobiResolved",
|
|
578
|
+
url,
|
|
579
|
+
cid: meta.cid ?? undefined,
|
|
580
|
+
role: meta.role ?? undefined,
|
|
581
|
+
eid: meta.eid ?? undefined,
|
|
582
|
+
});
|
|
583
|
+
return "resolved";
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
// An unreachable relay is the same wait as a relay that has not resolved the job yet.
|
|
588
|
+
if (error instanceof TransportError && !(error instanceof RelayOnlyError))
|
|
589
|
+
return "waiting";
|
|
590
|
+
// A relay answering what cannot be ingested fails the job, as a direct fetch does.
|
|
591
|
+
const failure = oobiJobFailure(error);
|
|
592
|
+
if (failure === null)
|
|
593
|
+
throw error;
|
|
594
|
+
return failure;
|
|
595
|
+
}
|
|
596
|
+
finally {
|
|
597
|
+
// Remove only a row this fetch left; a pass that changed nothing writes nothing.
|
|
598
|
+
if (this.hby.db.coobi.get(url))
|
|
599
|
+
this.hby.db.coobi.rem(url);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* The local identifier that asks the relay to resolve an OOBI: one the relay serves, which has authorized the
|
|
604
|
+
* relay as its mailbox ({@link relayAuthorized}). A group cannot sign the request alone, so groups never ask.
|
|
605
|
+
*
|
|
606
|
+
* @param relay The configured relay agent.
|
|
607
|
+
* @returns The asking habitat, or null while the relay has no accepted location or no local identifier it serves.
|
|
608
|
+
*/
|
|
609
|
+
relayAsker(relay) {
|
|
610
|
+
if (relayRefusal(this.hby, relay, null))
|
|
611
|
+
return null;
|
|
612
|
+
for (const hab of this.hby.habs.values()) {
|
|
613
|
+
const pre = hab.pre;
|
|
614
|
+
if (pre && pre !== relay.aid && !this.hby.db.groups.has(pre) && relayAuthorized(this.hby, pre, relay)) {
|
|
615
|
+
return hab;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return null;
|
|
359
619
|
}
|
|
360
620
|
/** Handle schema data-OOBIs before generic JSON reply handling. */
|
|
361
621
|
processSchemaOobiResponse(url, record, bytes) {
|
|
@@ -401,18 +661,19 @@ export class Oobiery {
|
|
|
401
661
|
/** Fan one multi-OOBI reply out into child `oobis.` jobs and a parent `moobi.` record. */
|
|
402
662
|
processMultiOobiReply(url, record, serder) {
|
|
403
663
|
const data = serder.ked?.a;
|
|
404
|
-
const cid =
|
|
664
|
+
const cid = remotePrefix(data?.aid);
|
|
405
665
|
const urls = Array.isArray(data?.urls)
|
|
406
666
|
? [
|
|
407
667
|
...new Set(data.urls.filter((entry) => typeof entry === "string")),
|
|
408
668
|
]
|
|
409
669
|
: [];
|
|
670
|
+
// A child the HTTP client would refuse fails the whole reply, before the first queue write: no partial fanout.
|
|
410
671
|
if (!cid || cid !== (record.cid ?? parseOobiUrl(url).cid ?? null)
|
|
411
|
-
|| urls.length === 0) {
|
|
672
|
+
|| urls.length === 0 || !urls.every(isRequestableHttpUrl)) {
|
|
412
673
|
this.failFetchedOobi(url, record, "invalid-multi-oobi");
|
|
413
674
|
return;
|
|
414
675
|
}
|
|
415
|
-
// Normalize every child before the first queue write
|
|
676
|
+
// Normalize every child before the first queue write.
|
|
416
677
|
const children = urls.map((childUrl) => {
|
|
417
678
|
const meta = parseOobiUrl(childUrl, record.oobialias ?? undefined);
|
|
418
679
|
return { url: childUrl, kind: queueKindFor(childUrl), meta, record: this.queuedRecord(meta) };
|
|
@@ -496,15 +757,15 @@ export class Oobiery {
|
|
|
496
757
|
throw new ValidationError(`Unsupported route=${args.route} in ${Ilks.rpy} reply.`);
|
|
497
758
|
}
|
|
498
759
|
const data = args.serder.ked?.a;
|
|
499
|
-
const cid =
|
|
760
|
+
const cid = remotePrefix(data?.cid);
|
|
500
761
|
const oobi = typeof data?.oobi === "string" ? data.oobi : null;
|
|
501
762
|
const dt = typeof args.serder.ked?.dt === "string" ? args.serder.ked.dt : new Date().toISOString();
|
|
502
763
|
if (!cid || !oobi) {
|
|
503
|
-
throw new ValidationError("Missing cid/oobi in /introduce reply.");
|
|
764
|
+
throw new ValidationError("Missing or invalid cid/oobi in /introduce reply.");
|
|
504
765
|
}
|
|
505
|
-
|
|
506
|
-
if (
|
|
507
|
-
throw new ValidationError(`
|
|
766
|
+
// Refused at intake: a URL the HTTP client refuses by construction can never resolve.
|
|
767
|
+
if (!isRequestableHttpUrl(oobi)) {
|
|
768
|
+
throw new ValidationError(`Introduced OOBI ${oobi} is not an HTTP(S) URL without credentials.`);
|
|
508
769
|
}
|
|
509
770
|
const proof = this.rvy.decideReply({
|
|
510
771
|
serder: args.serder,
|
|
@@ -541,8 +802,17 @@ export class Oobiery {
|
|
|
541
802
|
return acceptReplyDecision();
|
|
542
803
|
}
|
|
543
804
|
}
|
|
544
|
-
|
|
545
|
-
|
|
805
|
+
/**
|
|
806
|
+
* Register the `/oobis` exchange handler.
|
|
807
|
+
*
|
|
808
|
+
* @param hby Borrowed local identities and the durable OOBI queue.
|
|
809
|
+
* @param exchanger Exchanger receiving the route.
|
|
810
|
+
* @param notifier Optional operator notice producer.
|
|
811
|
+
* @param relaying Whether this host relays (the agent side of relay mode): requests are then accepted only from a
|
|
812
|
+
* served sender.
|
|
813
|
+
*/
|
|
814
|
+
export function loadOobiHandlers(hby, exchanger, notifier = null, relaying = false) {
|
|
815
|
+
exchanger.addHandler(new OobiRequestHandler(hby, notifier, relaying));
|
|
546
816
|
}
|
|
547
817
|
/**
|
|
548
818
|
* Peer-to-peer OOBI request handler for `/oobis` exchange messages.
|
|
@@ -557,15 +827,27 @@ export function loadOobiHandlers(hby, exchanger, notifier = null) {
|
|
|
557
827
|
*
|
|
558
828
|
* Local `keri-ts` adaptation:
|
|
559
829
|
* - verification stays lightweight and route-local: we only require a string
|
|
560
|
-
* `oobi` payload
|
|
830
|
+
* `oobi` payload that is an `http:` or `https:` URL without embedded
|
|
831
|
+
* credentials, which is what the HTTP client can request
|
|
561
832
|
* - durable follow-on work still belongs to `Oobiery` itself; this handler is
|
|
562
833
|
* just the exchange ingress seam that translates an EXN into queued resolver
|
|
563
834
|
* state
|
|
564
835
|
* - unlike KERIpy's DoDoer-centric runtime, the queued record is later
|
|
565
836
|
* advanced by the shared `AgentRuntime` turn that owns OOBI processing
|
|
837
|
+
* - on a relaying host (the agent side of relay mode) a request is accepted
|
|
838
|
+
* only from a served sender, by the same rule `/fwd` intake relays for
|
|
839
|
+
* ({@link servingHost}): the sender authorized a local habitat as its
|
|
840
|
+
* `mailbox` or `agent`. An edge resolves third-party OOBIs this way; nobody
|
|
841
|
+
* else can make the agent fetch a URL. Without relaying, any authenticated
|
|
842
|
+
* sender is accepted, as in KERIpy.
|
|
566
843
|
*/
|
|
567
844
|
export class OobiRequestHandler {
|
|
568
|
-
|
|
845
|
+
/**
|
|
846
|
+
* @param hby Borrowed local identities and the durable OOBI queue.
|
|
847
|
+
* @param notifier Optional operator notice producer.
|
|
848
|
+
* @param relaying Restrict requests to served senders (a relaying host); false accepts any sender.
|
|
849
|
+
*/
|
|
850
|
+
constructor(hby, notifier = null, relaying = false) {
|
|
569
851
|
Object.defineProperty(this, "resource", {
|
|
570
852
|
enumerable: true,
|
|
571
853
|
configurable: true,
|
|
@@ -584,21 +866,30 @@ export class OobiRequestHandler {
|
|
|
584
866
|
writable: true,
|
|
585
867
|
value: void 0
|
|
586
868
|
});
|
|
869
|
+
/** Whether requests are restricted to served senders. */
|
|
870
|
+
Object.defineProperty(this, "relaying", {
|
|
871
|
+
enumerable: true,
|
|
872
|
+
configurable: true,
|
|
873
|
+
writable: true,
|
|
874
|
+
value: void 0
|
|
875
|
+
});
|
|
587
876
|
this.hby = hby;
|
|
588
877
|
this.notifier = notifier;
|
|
878
|
+
this.relaying = relaying;
|
|
589
879
|
}
|
|
880
|
+
/**
|
|
881
|
+
* Accept a request carrying an `oobi` the HTTP client can request ({@link isRequestableHttpUrl}: HTTP(S), no
|
|
882
|
+
* embedded credentials); on a relaying host, only from a served sender. Anything else could never resolve.
|
|
883
|
+
*
|
|
884
|
+
* @param args Authenticated exchange and the receiving hosted endpoint, if the request named one.
|
|
885
|
+
* @returns False to reject the exchange.
|
|
886
|
+
*/
|
|
590
887
|
verify(args) {
|
|
591
|
-
|
|
592
|
-
if (typeof payload?.["oobi"] !== "string") {
|
|
593
|
-
return false;
|
|
594
|
-
}
|
|
595
|
-
try {
|
|
596
|
-
const parsed = new URL(payload.oobi);
|
|
597
|
-
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
598
|
-
}
|
|
599
|
-
catch {
|
|
888
|
+
if (this.relaying && !servingHost(this.hby, { sender: args.serder.pre, origin: null }, args.mailboxAid)) {
|
|
600
889
|
return false;
|
|
601
890
|
}
|
|
891
|
+
const payload = args.serder.ked?.a;
|
|
892
|
+
return typeof payload?.["oobi"] === "string" && isRequestableHttpUrl(payload.oobi);
|
|
602
893
|
}
|
|
603
894
|
handle(args) {
|
|
604
895
|
const src = args.serder.pre;
|
|
@@ -698,6 +989,48 @@ function findPathSequence(parts, sequence) {
|
|
|
698
989
|
}
|
|
699
990
|
return -1;
|
|
700
991
|
}
|
|
992
|
+
/**
|
|
993
|
+
* Path, relative to the relay's location, at which the relay serves what an OOBI job names. The relay's OOBI
|
|
994
|
+
* endpoint answers for what it holds, so the third party's host and base path are dropped:
|
|
995
|
+
* - a controller, witness, agent or mailbox OOBI `.../oobi/{cid}/{role}[/{eid}]` maps to
|
|
996
|
+
* `oobi/{cid}/{role}[/{eid}]`; the relay answers with the controller's KEL (receipts included) and the replies
|
|
997
|
+
* it holds for that role;
|
|
998
|
+
* - a data OOBI `.../oobi/{said}` (a schema) maps to `oobi/{said}`.
|
|
999
|
+
* Well-known OOBIs are authentication jobs, not Oobiery jobs. A URL naming neither maps to nothing.
|
|
1000
|
+
*
|
|
1001
|
+
* @param job Parsed job metadata.
|
|
1002
|
+
* @returns The relative path, or null when the job names no identifier or SAID.
|
|
1003
|
+
*/
|
|
1004
|
+
function relayOobiPath(job) {
|
|
1005
|
+
if (job.cid && job.role) {
|
|
1006
|
+
return ["oobi", job.cid, job.role, ...(job.eid ? [job.eid] : [])].map(encodeURIComponent).join("/");
|
|
1007
|
+
}
|
|
1008
|
+
if (job.said)
|
|
1009
|
+
return `oobi/${encodeURIComponent(job.said)}`;
|
|
1010
|
+
return null;
|
|
1011
|
+
}
|
|
1012
|
+
/** A base URL that resolves relative paths beneath it rather than beside its last segment. */
|
|
1013
|
+
function withSlash(url) {
|
|
1014
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* The qualified prefix a remote reply names, or null when the value is not a string or does not decode as one.
|
|
1018
|
+
*
|
|
1019
|
+
* @param value Remote field value.
|
|
1020
|
+
* @returns The prefix's qb64, or null.
|
|
1021
|
+
*/
|
|
1022
|
+
function remotePrefix(value) {
|
|
1023
|
+
if (typeof value !== "string")
|
|
1024
|
+
return null;
|
|
1025
|
+
try {
|
|
1026
|
+
return new Prefixer({ qb64: value }).qb64;
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
if (error instanceof ParserError)
|
|
1030
|
+
return null;
|
|
1031
|
+
throw error;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
701
1034
|
function queueKindFor(url) {
|
|
702
1035
|
return isWellKnownOobiUrl(url) ? "woobi" : "oobis";
|
|
703
1036
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Finite credential-bundle intake; protocol owners retain and recover their own pending evidence. */
|
|
2
2
|
import { Ilks, parse, SerderACDC, SerderKERI } from "cesr-ts";
|
|
3
3
|
import { action, call, ensure, race } from "effection";
|
|
4
|
+
import { validateRip, validateUpdate } from "../../acdc/regeventing.js";
|
|
4
5
|
import { consoleLogger } from "../../core/logger.js";
|
|
5
6
|
import { rethrowCleanupFailure } from "../runtime/abortable.js";
|
|
6
7
|
import { chunkBytes } from "../transport/byte-source.js";
|
|
@@ -10,6 +11,14 @@ import { chunkBytes } from "../transport/byte-source.js";
|
|
|
10
11
|
* Later input failures preserve earlier accepted state. Receipts, replies and other messages still
|
|
11
12
|
* use normal dispatch; absence of an importer acceptance lookup is not an additional refusal policy.
|
|
12
13
|
*
|
|
14
|
+
* The input is retained history the caller already holds (for example an exported disclosure stream: KELs, the v2
|
|
15
|
+
* registry TEL, a Grant and its Admit), so every frame is dispatched with `DispatchOptions.retained`. Its v2
|
|
16
|
+
* exchanges skip KRAM, whose window their original stamps passed long ago; signatures, route verification and
|
|
17
|
+
* escrows are unchanged, and the accepted exchanges raise no notice and get no exchange lifetime. Targets: raw
|
|
18
|
+
* credentials, KEL and v1 TEL events, v2 `rip`/`bup` (accepted at their exact ordinal in the v2 registry store),
|
|
19
|
+
* and exchanges (accepted by the Exchanger; a v2 Admit also needs its Grant's reply row to name it). Importing the
|
|
20
|
+
* same input again changes nothing and reports the same acceptance.
|
|
21
|
+
*
|
|
13
22
|
* @param protocol Borrowed protocol dispatcher, sharing reger with its TEL/credential handlers.
|
|
14
23
|
* @param reger Borrowed credential/TEL store whose accepted records establish the result.
|
|
15
24
|
* @param bytes Borrowed finite CESR bundle; parser chunks are copied during intake.
|
|
@@ -120,7 +129,8 @@ function readImportTargets(protocol, reger, bytes, kvy) {
|
|
|
120
129
|
if (target)
|
|
121
130
|
targets.set(target.said, target);
|
|
122
131
|
}
|
|
123
|
-
|
|
132
|
+
// The one dispatch that marks its input as retained history; see the operation docs.
|
|
133
|
+
const { decision } = yield* protocol.dispatch(next.value, { retained: true }, kvy);
|
|
124
134
|
if (decision && (decision.kind === "reject" || decision.kind === "drop")) {
|
|
125
135
|
consoleLogger.warn("Credential import message refused", {
|
|
126
136
|
said: serder.said,
|
|
@@ -148,7 +158,16 @@ function readImportTargets(protocol, reger, bytes, kvy) {
|
|
|
148
158
|
function importTarget(serder, reger) {
|
|
149
159
|
const said = serder.said, ilk = serder.ilk;
|
|
150
160
|
if (serder instanceof SerderACDC) {
|
|
151
|
-
|
|
161
|
+
if (ilk === null)
|
|
162
|
+
return { ilk, said, savedBefore: reger.saved.get([said]) !== null };
|
|
163
|
+
if (ilk !== Ilks.rip && ilk !== Ilks.bup)
|
|
164
|
+
return null;
|
|
165
|
+
// An event that fails field validation, or whose ordinal no store can hold, keeps no coordinates, so it is
|
|
166
|
+
// always reported unaccepted.
|
|
167
|
+
const result = ilk === Ilks.rip ? validateRip(serder) : validateUpdate(serder, serder.regid ?? "");
|
|
168
|
+
return result.kind === "valid" && result.event.sn <= BigInt(Number.MAX_SAFE_INTEGER)
|
|
169
|
+
? { ilk, said, regid: result.event.regid, sn: Number(result.event.sn) }
|
|
170
|
+
: { ilk, said, regid: null, sn: null };
|
|
152
171
|
}
|
|
153
172
|
switch (ilk) {
|
|
154
173
|
case Ilks.icp:
|
|
@@ -164,7 +183,11 @@ function importTarget(serder, reger) {
|
|
|
164
183
|
case Ilks.brv:
|
|
165
184
|
return { ilk, said, pre: serder.pre, sn: serder.sn };
|
|
166
185
|
case Ilks.exn:
|
|
167
|
-
|
|
186
|
+
case Ilks.xip: {
|
|
187
|
+
const prior = serder.sad?.p;
|
|
188
|
+
const admit = serder.pvrsn.major >= 2 && serder.route === "/ipex/admit" && typeof prior === "string";
|
|
189
|
+
return { ilk, said, answers: admit ? prior : null };
|
|
190
|
+
}
|
|
168
191
|
default:
|
|
169
192
|
return null;
|
|
170
193
|
}
|
|
@@ -195,7 +218,13 @@ function importTargetAccepted(target, protocol, reger) {
|
|
|
195
218
|
case Ilks.bis:
|
|
196
219
|
case Ilks.brv:
|
|
197
220
|
return target.pre !== null && target.sn !== null && reger.tels.getOn(target.pre, target.sn)?.qb64 === target.said;
|
|
221
|
+
case Ilks.rip:
|
|
222
|
+
case Ilks.bup:
|
|
223
|
+
return target.regid !== null && target.sn !== null
|
|
224
|
+
&& protocol.registry?.store.seqSaider(target.regid, target.sn)?.qb64 === target.said;
|
|
198
225
|
case Ilks.exn:
|
|
199
|
-
|
|
226
|
+
case Ilks.xip:
|
|
227
|
+
return protocol.exc.complete(target.said)
|
|
228
|
+
&& (target.answers === null || protocol.kvy.db.erpy.get([target.answers])?.qb64 === target.said);
|
|
200
229
|
}
|
|
201
230
|
}
|