@actana/sdk 0.3.2 → 0.4.1
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/README.md +2 -1
- package/dist/core-client.d.ts +71 -0
- package/dist/core-client.d.ts.map +1 -1
- package/dist/core-client.js +94 -5
- package/dist/core-client.js.map +1 -1
- package/dist/core-link-frames.d.ts +160 -8
- package/dist/core-link-frames.d.ts.map +1 -1
- package/dist/core-link-frames.js +84 -3
- package/dist/core-link-frames.js.map +1 -1
- package/dist/core-pairing-csr.d.ts +21 -0
- package/dist/core-pairing-csr.d.ts.map +1 -0
- package/dist/core-pairing-csr.js +159 -0
- package/dist/core-pairing-csr.js.map +1 -0
- package/dist/core-pairing-wire.d.ts +63 -0
- package/dist/core-pairing-wire.d.ts.map +1 -0
- package/dist/core-pairing-wire.js +22 -0
- package/dist/core-pairing-wire.js.map +1 -0
- package/dist/core-pairing.d.ts +211 -0
- package/dist/core-pairing.d.ts.map +1 -0
- package/dist/core-pairing.js +630 -0
- package/dist/core-pairing.js.map +1 -0
- package/dist/core-session.d.ts +282 -4
- package/dist/core-session.d.ts.map +1 -1
- package/dist/core-session.js +397 -36
- package/dist/core-session.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { CoreRegistrationBlob } from "./core-registration-blob.ts";
|
|
2
|
+
export { CORE_PAIRING_REDEEM_PATH, type CorePairingClientInfo, type CorePairingRedeemRequest, type CorePairingRedeemResponse, type CorePairingRefusalBody, } from "./core-pairing-wire.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Why a pairing attempt did not produce a blob.
|
|
5
|
+
*
|
|
6
|
+
* The list is what a caller can *act* on, and it stops where the Core's own
|
|
7
|
+
* answers stop. `refused` covers a wrong code, an expired session, one already
|
|
8
|
+
* redeemed and one whose attempts are spent, because the Core answers all four
|
|
9
|
+
* with one status and one body on purpose (`core-pairing-routes.ts`, "every
|
|
10
|
+
* refusal is the same refusal"): telling them apart on the wire would tell an
|
|
11
|
+
* attacker whether a session exists and whether their last guess was closer.
|
|
12
|
+
* A client that reported four different things would be inventing three of
|
|
13
|
+
* them. The distinction the operator needs is in the Core's audit log, which
|
|
14
|
+
* is where #282 put it deliberately.
|
|
15
|
+
*/
|
|
16
|
+
export type CorePairingFailure =
|
|
17
|
+
/** The address could not be read as a Core's HTTPS or core-link address. */
|
|
18
|
+
"bad-address"
|
|
19
|
+
/** The code was not eight characters, or named no pairing session. */
|
|
20
|
+
| "bad-code"
|
|
21
|
+
/** The expected fingerprint was not a SHA-256 fingerprint. */
|
|
22
|
+
| "bad-fingerprint"
|
|
23
|
+
/** Nothing answered at that address, or the dial timed out. */
|
|
24
|
+
| "unreachable"
|
|
25
|
+
/** The Core presented a chain with no CA in it — nothing to pin. */
|
|
26
|
+
| "no-ca-presented"
|
|
27
|
+
/** No fingerprint was given to check against. The code was not sent. */
|
|
28
|
+
| "fingerprint-unconfirmed"
|
|
29
|
+
/** The presented CA is not the expected one. The code was not sent. */
|
|
30
|
+
| "fingerprint-mismatch"
|
|
31
|
+
/**
|
|
32
|
+
* The expected CA, on an address its certificate does not cover.
|
|
33
|
+
*
|
|
34
|
+
* Its own failure rather than a mismatch, because it is not an attack and
|
|
35
|
+
* saying so sends the operator hunting for one: a Core's server certificate
|
|
36
|
+
* covers the host it was set up for plus loopback, so reaching a Core over a
|
|
37
|
+
* second interface, a tunnel or a DNS name added later fails here with the
|
|
38
|
+
* fingerprint matching perfectly.
|
|
39
|
+
*/
|
|
40
|
+
| "hostname-mismatch"
|
|
41
|
+
/** The expected CA, and a certificate that is expired or otherwise unusable. */
|
|
42
|
+
| "certificate-invalid"
|
|
43
|
+
/** Wrong, expired, already redeemed, or out of attempts — see above. */
|
|
44
|
+
| "refused"
|
|
45
|
+
/** Too many attempts from this client, too fast. Try again later. */
|
|
46
|
+
| "rate-limited"
|
|
47
|
+
/** The Core would not accept the request itself — a bug on this side. */
|
|
48
|
+
| "rejected"
|
|
49
|
+
/** There is no pairing endpoint on that Core. */
|
|
50
|
+
| "not-pairable"
|
|
51
|
+
/** The Core failed while handling the redemption. */
|
|
52
|
+
| "core-error"
|
|
53
|
+
/** A 200 that was not a redemption response. */
|
|
54
|
+
| "malformed-response";
|
|
55
|
+
/** Everything a failure knows beyond its {@link CorePairingFailure}. */
|
|
56
|
+
export type CorePairingErrorDetail = {
|
|
57
|
+
/** The HTTP status, when the failure came from one. */
|
|
58
|
+
status?: number;
|
|
59
|
+
/** `retry-after`, in seconds, on a `rate-limited` failure. */
|
|
60
|
+
retryAfterSeconds?: number;
|
|
61
|
+
/** The Core's own refusal code, when it sent one. */
|
|
62
|
+
coreCode?: string;
|
|
63
|
+
/** The fingerprint the caller was told to expect. */
|
|
64
|
+
expectedFingerprint?: string;
|
|
65
|
+
/** The fingerprint the Core actually presented. */
|
|
66
|
+
presentedFingerprint?: string;
|
|
67
|
+
/** The CA the Core presented, PEM — for a UI that wants to show it. */
|
|
68
|
+
presentedCaCert?: string;
|
|
69
|
+
/** The TLS or OpenSSL code behind a dial failure, e.g. `CERT_HAS_EXPIRED`. */
|
|
70
|
+
tlsCode?: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* A pairing attempt that did not produce a blob.
|
|
74
|
+
*
|
|
75
|
+
* One class with a {@link CorePairingFailure} rather than a class per failure:
|
|
76
|
+
* the CLI (#285) and the Panel (#286) both switch on the reason to write a
|
|
77
|
+
* sentence, and a `switch` over a union is checked by the compiler where a
|
|
78
|
+
* chain of `instanceof` is not.
|
|
79
|
+
*/
|
|
80
|
+
export declare class CorePairingError extends Error {
|
|
81
|
+
readonly name = "CorePairingError";
|
|
82
|
+
/** Which failure this is. Switch on it; do not read the message. */
|
|
83
|
+
readonly failure: CorePairingFailure;
|
|
84
|
+
/** Whatever the failure knows beyond its reason. */
|
|
85
|
+
readonly detail: CorePairingErrorDetail;
|
|
86
|
+
constructor(failure: CorePairingFailure, message: string, detail?: CorePairingErrorDetail, options?: {
|
|
87
|
+
cause?: unknown;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** What a bootstrap dial learns about a Core before anything is trusted. */
|
|
91
|
+
export type CorePairingIdentity = {
|
|
92
|
+
/** SHA-256 over the CA's DER, colon-separated uppercase hex. */
|
|
93
|
+
fingerprint: string;
|
|
94
|
+
/** The PEM CA certificate that fingerprint is of. */
|
|
95
|
+
caCert: string;
|
|
96
|
+
/** The host that was dialled. */
|
|
97
|
+
host: string;
|
|
98
|
+
/** The port that was dialled. */
|
|
99
|
+
port: number;
|
|
100
|
+
/** `https://host:port` — where a redemption would be posted. */
|
|
101
|
+
httpsOrigin: string;
|
|
102
|
+
};
|
|
103
|
+
/** How long a dial or a redemption may take before it is called unreachable. */
|
|
104
|
+
export declare const DEFAULT_PAIRING_TIMEOUT_MS = 15000;
|
|
105
|
+
/**
|
|
106
|
+
* Dial a Core and report the CA it presents — **without a code to send**.
|
|
107
|
+
*
|
|
108
|
+
* This is the first-contact mode, and the reason it is a separate function
|
|
109
|
+
* rather than a flag is that it takes no code: a UI that shows the operator's
|
|
110
|
+
* fingerprint beside the Core's, and asks a human whether they match, cannot
|
|
111
|
+
* leak a secret it was never given. {@link pairWithCore} calls it too, so the
|
|
112
|
+
* fingerprint a caller confirms is computed by the same code that later
|
|
113
|
+
* enforces it.
|
|
114
|
+
*
|
|
115
|
+
* The dial is unverified, because at this point in the flow there is nothing to
|
|
116
|
+
* verify against — that is what the fingerprint the operator read out is for.
|
|
117
|
+
* Nothing is sent on this connection and it is closed as soon as the chain has
|
|
118
|
+
* been read.
|
|
119
|
+
*/
|
|
120
|
+
export declare function fetchCorePairingIdentity(opts: {
|
|
121
|
+
/** `host:port`, `https://host:port` or `wss://host:port`. */
|
|
122
|
+
address: string;
|
|
123
|
+
/** Defaults to {@link DEFAULT_PAIRING_TIMEOUT_MS}. */
|
|
124
|
+
timeoutMs?: number;
|
|
125
|
+
}): Promise<CorePairingIdentity>;
|
|
126
|
+
export type PairWithCoreOptions = {
|
|
127
|
+
/** The Core's address: `host:port`, `https://host:port` or `wss://host:port`. */
|
|
128
|
+
address: string;
|
|
129
|
+
/**
|
|
130
|
+
* The pairing code the operator read out — `XXXX-XXXX`, in any case and with
|
|
131
|
+
* or without the hyphen.
|
|
132
|
+
*
|
|
133
|
+
* A code names a session, and the Core will not go looking for which one, so
|
|
134
|
+
* the session id has to travel with it. Either pass it as `sessionId`, or
|
|
135
|
+
* pass a single `<sessionId>:<XXXX-XXXX>` string here and this reads both out
|
|
136
|
+
* of it. Which of the two an operator is given is #280's to settle across
|
|
137
|
+
* `actana pair new` (#283) and `actana core pair` (#285); both forms parse
|
|
138
|
+
* here so that neither is a change to this module.
|
|
139
|
+
*/
|
|
140
|
+
code: string;
|
|
141
|
+
/** The pairing session the code belongs to, when `code` does not carry it. */
|
|
142
|
+
sessionId?: string;
|
|
143
|
+
/**
|
|
144
|
+
* The CA fingerprint the operator read out, in any of the forms a human
|
|
145
|
+
* copies it in: colon-separated or not, upper or lower case.
|
|
146
|
+
*
|
|
147
|
+
* **Absent is not "skip the check".** With no fingerprint this function
|
|
148
|
+
* refuses with `fingerprint-unconfirmed` and reports the presented one in the
|
|
149
|
+
* error, having sent no code — see {@link fetchCorePairingIdentity}.
|
|
150
|
+
*/
|
|
151
|
+
expectedCaFingerprint?: string | null;
|
|
152
|
+
/** What this machine calls itself, for the operator's `actana pair ls`. */
|
|
153
|
+
label?: string;
|
|
154
|
+
/** This machine's platform, e.g. `process.platform`. */
|
|
155
|
+
platform?: string;
|
|
156
|
+
/** Defaults to {@link DEFAULT_PAIRING_TIMEOUT_MS}, per connection. */
|
|
157
|
+
timeoutMs?: number;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Pair with a Core and return the credential it issued.
|
|
161
|
+
*
|
|
162
|
+
* The result is a {@link CoreRegistrationBlob} and nothing downstream can tell
|
|
163
|
+
* it from a hand-carried one: `coreConnectionFromBlob` unpacks it,
|
|
164
|
+
* `httpsBaseUrlFor` derives the HTTPS origin, the PEMs go into the mTLS
|
|
165
|
+
* handshake and the bearer into the `auth` frame — exactly as they do today.
|
|
166
|
+
* `clientKey` is the key generated on this machine a few lines above; it was
|
|
167
|
+
* never sent, and the Core has never seen it.
|
|
168
|
+
*
|
|
169
|
+
* Throws {@link CorePairingError} for everything that is not a blob.
|
|
170
|
+
*/
|
|
171
|
+
export declare function pairWithCore(opts: PairWithCoreOptions): Promise<CoreRegistrationBlob>;
|
|
172
|
+
/** A pairing code, and the session it names. */
|
|
173
|
+
export type PairingTicket = {
|
|
174
|
+
sessionId: string;
|
|
175
|
+
code: string;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Read a ticket out of what a human typed.
|
|
179
|
+
*
|
|
180
|
+
* The code is checked for *shape* — eight alphanumerics, hyphens and spaces
|
|
181
|
+
* ignored — and not against the Core's alphabet. That is a deliberate stop:
|
|
182
|
+
* the alphabet is an internal of `packages/shared` (which this package may not
|
|
183
|
+
* import) and mirroring it here would be a copy free to drift, which ADR 0025
|
|
184
|
+
* D3 is about. What the shape check buys is worth having on its own: a code
|
|
185
|
+
* that could not be right whatever the alphabet is never spends one of the
|
|
186
|
+
* five attempts the operator's session has.
|
|
187
|
+
*/
|
|
188
|
+
export declare function parsePairingTicket(input: string, sessionId?: string): PairingTicket;
|
|
189
|
+
/** A Core's address, in the two forms this module needs it. */
|
|
190
|
+
type CoreAddress = {
|
|
191
|
+
host: string;
|
|
192
|
+
port: number;
|
|
193
|
+
httpsOrigin: string;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Read `host:port`, `https://host:port` or `wss://host:port`.
|
|
197
|
+
*
|
|
198
|
+
* `ws://` and `http://` are refused rather than upgraded: there is no
|
|
199
|
+
* certificate on a plaintext dial, so there is no fingerprint to check, and
|
|
200
|
+
* pairing over one would be the silent unverified exchange this module exists
|
|
201
|
+
* to make impossible. A caller that meant the secure port should say so.
|
|
202
|
+
*/
|
|
203
|
+
export declare function parseCoreAddress(address: string): CoreAddress;
|
|
204
|
+
/**
|
|
205
|
+
* A fingerprint as this module compares them: colon-separated uppercase hex,
|
|
206
|
+
* which is the form `actana pair new` prints and a human copies.
|
|
207
|
+
*/
|
|
208
|
+
export declare function parseFingerprint(input: string): string;
|
|
209
|
+
/** SHA-256 over a certificate's DER, in the form {@link parseFingerprint} yields. */
|
|
210
|
+
export declare function fingerprintOf(der: Uint8Array): string;
|
|
211
|
+
//# sourceMappingURL=core-pairing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-pairing.d.ts","sourceRoot":"","sources":["../src/core-pairing.ts"],"names":[],"mappings":"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"}
|