euclid-ndk 0.2.0
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/LICENSE +201 -0
- package/README.md +603 -0
- package/dist/auth/index.d.ts +18 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +17 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/rfc9421.d.ts +112 -0
- package/dist/auth/rfc9421.d.ts.map +1 -0
- package/dist/auth/rfc9421.js +452 -0
- package/dist/auth/rfc9421.js.map +1 -0
- package/dist/auth/scheme.d.ts +46 -0
- package/dist/auth/scheme.d.ts.map +1 -0
- package/dist/auth/scheme.js +44 -0
- package/dist/auth/scheme.js.map +1 -0
- package/dist/auth/signable.d.ts +37 -0
- package/dist/auth/signable.d.ts.map +1 -0
- package/dist/auth/signable.js +70 -0
- package/dist/auth/signable.js.map +1 -0
- package/dist/auth/sigv4.d.ts +72 -0
- package/dist/auth/sigv4.d.ts.map +1 -0
- package/dist/auth/sigv4.js +227 -0
- package/dist/auth/sigv4.js.map +1 -0
- package/dist/credentials.d.ts +56 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +135 -0
- package/dist/credentials.js.map +1 -0
- package/dist/dto/com.d.ts +101 -0
- package/dist/dto/com.d.ts.map +1 -0
- package/dist/dto/com.js +132 -0
- package/dist/dto/com.js.map +1 -0
- package/dist/dto/eag.d.ts +104 -0
- package/dist/dto/eag.d.ts.map +1 -0
- package/dist/dto/eag.js +70 -0
- package/dist/dto/eag.js.map +1 -0
- package/dist/dto/eam.d.ts +105 -0
- package/dist/dto/eam.d.ts.map +1 -0
- package/dist/dto/eam.js +98 -0
- package/dist/dto/eam.js.map +1 -0
- package/dist/dto/eap.d.ts +88 -0
- package/dist/dto/eap.d.ts.map +1 -0
- package/dist/dto/eap.js +54 -0
- package/dist/dto/eap.js.map +1 -0
- package/dist/dto/ekm.d.ts +109 -0
- package/dist/dto/ekm.d.ts.map +1 -0
- package/dist/dto/ekm.js +78 -0
- package/dist/dto/ekm.js.map +1 -0
- package/dist/dto/ekv.d.ts +73 -0
- package/dist/dto/ekv.d.ts.map +1 -0
- package/dist/dto/ekv.js +45 -0
- package/dist/dto/ekv.js.map +1 -0
- package/dist/dto/ens.d.ts +84 -0
- package/dist/dto/ens.d.ts.map +1 -0
- package/dist/dto/ens.js +72 -0
- package/dist/dto/ens.js.map +1 -0
- package/dist/dto/eqs.d.ts +160 -0
- package/dist/dto/eqs.d.ts.map +1 -0
- package/dist/dto/eqs.js +120 -0
- package/dist/dto/eqs.js.map +1 -0
- package/dist/dto/esm.d.ts +192 -0
- package/dist/dto/esm.d.ts.map +1 -0
- package/dist/dto/esm.js +147 -0
- package/dist/dto/esm.js.map +1 -0
- package/dist/dto/ess.d.ts +49 -0
- package/dist/dto/ess.d.ts.map +1 -0
- package/dist/dto/ess.js +30 -0
- package/dist/dto/ess.js.map +1 -0
- package/dist/dto/json.d.ts +24 -0
- package/dist/dto/json.d.ts.map +1 -0
- package/dist/dto/json.js +47 -0
- package/dist/dto/json.js.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +73 -0
- package/dist/errors.js.map +1 -0
- package/dist/http/client.d.ts +96 -0
- package/dist/http/client.d.ts.map +1 -0
- package/dist/http/client.js +258 -0
- package/dist/http/client.js.map +1 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/base.d.ts +121 -0
- package/dist/modules/base.d.ts.map +1 -0
- package/dist/modules/base.js +142 -0
- package/dist/modules/base.js.map +1 -0
- package/dist/modules/eag.d.ts +166 -0
- package/dist/modules/eag.d.ts.map +1 -0
- package/dist/modules/eag.js +189 -0
- package/dist/modules/eag.js.map +1 -0
- package/dist/modules/eam.d.ts +314 -0
- package/dist/modules/eam.d.ts.map +1 -0
- package/dist/modules/eam.js +641 -0
- package/dist/modules/eam.js.map +1 -0
- package/dist/modules/eap.d.ts +204 -0
- package/dist/modules/eap.d.ts.map +1 -0
- package/dist/modules/eap.js +218 -0
- package/dist/modules/eap.js.map +1 -0
- package/dist/modules/ekm.d.ts +182 -0
- package/dist/modules/ekm.d.ts.map +1 -0
- package/dist/modules/ekm.js +228 -0
- package/dist/modules/ekm.js.map +1 -0
- package/dist/modules/ekv.d.ts +189 -0
- package/dist/modules/ekv.d.ts.map +1 -0
- package/dist/modules/ekv.js +212 -0
- package/dist/modules/ekv.js.map +1 -0
- package/dist/modules/ens.d.ts +122 -0
- package/dist/modules/ens.d.ts.map +1 -0
- package/dist/modules/ens.js +167 -0
- package/dist/modules/ens.js.map +1 -0
- package/dist/modules/eqs.d.ts +283 -0
- package/dist/modules/eqs.d.ts.map +1 -0
- package/dist/modules/eqs.js +355 -0
- package/dist/modules/eqs.js.map +1 -0
- package/dist/modules/esm.d.ts +358 -0
- package/dist/modules/esm.d.ts.map +1 -0
- package/dist/modules/esm.js +660 -0
- package/dist/modules/esm.js.map +1 -0
- package/dist/modules/ess.d.ts +96 -0
- package/dist/modules/ess.d.ts.map +1 -0
- package/dist/modules/ess.js +110 -0
- package/dist/modules/ess.js.map +1 -0
- package/dist/url.d.ts +31 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +47 -0
- package/dist/url.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
# euclid-ndk
|
|
2
|
+
|
|
3
|
+
Node.js client library for the [euclid](https://github.com/jensvogt/euclid) server.
|
|
4
|
+
|
|
5
|
+
Nine modules so far. EAM - euclid's access management module - is where a login comes from; ESM
|
|
6
|
+
(storage), EQS (queues), ENS (notifications), EKM (keys), EKV (tables), EAP (applications), ESS
|
|
7
|
+
(secrets) and EAG (the API gateway) are reached from the session it hands back. The remaining modules
|
|
8
|
+
(EES, ETS) speak the same protocol over the same client and will follow.
|
|
9
|
+
|
|
10
|
+
Requires Node 20 or newer, and **has no dependencies**. Installing this SDK does not bring a TLS
|
|
11
|
+
stack, an HTTP client and a JSON parser along with it: the wire protocol is JSON over HTTP and the
|
|
12
|
+
signatures are HMAC-SHA256, all of which node's standard library already covers. TypeScript is a
|
|
13
|
+
development dependency, not a runtime one - what npm ships is JavaScript with its declarations
|
|
14
|
+
beside it.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install euclid-ndk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
ESM only, with types: `import { Euclid } from "euclid-ndk"`. A CommonJS application can reach it
|
|
23
|
+
with `await import("euclid-ndk")`.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Log in once and reuse the session:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { Euclid } from "euclid-ndk";
|
|
31
|
+
|
|
32
|
+
const session = await Euclid.forServer("https://euclid.example.com").login("jens", "secret");
|
|
33
|
+
|
|
34
|
+
const users = await session.listUsers({ prefix: "j", pageSize: 25 });
|
|
35
|
+
for (const user of users.items) console.log(user.userId, user.email);
|
|
36
|
+
|
|
37
|
+
session.close();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The builder form takes the same options one at a time, which reads better when there are several:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { Euclid, RFC9421 } from "euclid-ndk";
|
|
44
|
+
|
|
45
|
+
const session = await Euclid.forServer("https://euclid.example.com")
|
|
46
|
+
.access()
|
|
47
|
+
.credentials("jens", "secret")
|
|
48
|
+
.namespace("development")
|
|
49
|
+
.signingScheme(RFC9421)
|
|
50
|
+
.caCertPath("/etc/euclid/euclid_cert.crt")
|
|
51
|
+
.login();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
A session holds a connection, so close it when you are done:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
try {
|
|
58
|
+
await session.createAccount("111", "acme", "an account");
|
|
59
|
+
} finally {
|
|
60
|
+
session.close();
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Every listing answers with a page - `total` is how many exist, `items` is the page:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
const { total, items } = await session.listAccounts({ pageSize: 5 });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The other modules hang off that session - `session.esm()`, `session.eqs()`, `session.ens()`,
|
|
71
|
+
`session.ekm()`, `session.ekv()`, `session.eap()`, `session.ess()`, `session.eag()` - and each answers
|
|
72
|
+
with the same client every time, so asking for one inside a loop costs one connection rather than one
|
|
73
|
+
per iteration:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const esm = session.esm();
|
|
77
|
+
const bucket = await esm.createBucket("reports");
|
|
78
|
+
await esm.uploadFile(bucket.ern, "2026/q3.pdf", "q3.pdf");
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### The credentials cache
|
|
82
|
+
|
|
83
|
+
`login()` writes `~/.euclid/credentials` and reads it back on the next call, so logging in twice
|
|
84
|
+
costs one round trip. It is the same file `euclid-cli`, `euclid-jdk` and `euclid-pdk` use, with the
|
|
85
|
+
same field names, so a login from any of the four is picked up by the others.
|
|
86
|
+
`EUCLID_CREDENTIALS_FILE` overrides the path, which is also how euclid hands a managed application
|
|
87
|
+
its own credentials.
|
|
88
|
+
|
|
89
|
+
Pass `useCache(false)` to force a fresh login and leave the file alone.
|
|
90
|
+
|
|
91
|
+
### Signing
|
|
92
|
+
|
|
93
|
+
A login returns two credentials: a bearer token, and - when the user has one - an access key and
|
|
94
|
+
secret. By default a session signs with the access key when it has one and presents the token
|
|
95
|
+
otherwise, which is what `AUTH_AUTO` means. euclid accepts either for every action.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { AUTH_BEARER } from "euclid-ndk";
|
|
99
|
+
|
|
100
|
+
const session = await Euclid.forServer(url).login("jens", "secret", { auth: AUTH_BEARER });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Two signing schemes are implemented, both keyed by the same access key and secret:
|
|
104
|
+
|
|
105
|
+
| Scheme | Where the signature travels | Notes |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `SIGV4` | `Authorization`, plus `x-amz-date` and `x-amz-content-sha256` | The default, and what euclid has understood from the start |
|
|
108
|
+
| `RFC9421` | `Signature` and `Signature-Input`, plus `Content-Digest` | [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) HTTP Message Signatures, the standard scheme meant to replace it |
|
|
109
|
+
|
|
110
|
+
Both cover a **fixed** set of headers rather than a set the request declares: the method, path and
|
|
111
|
+
authority, the body digest, and the `x-euclid-account-id`, `x-euclid-action`, `x-euclid-region`,
|
|
112
|
+
`x-euclid-target` and `x-euclid-user-id` headers that carry what the request is asking for and on
|
|
113
|
+
whose behalf. euclid's server compares that list against its own for exact equality, so a signature
|
|
114
|
+
covering more, fewer, or the same components in another order is rejected. `x-euclid-namespace` is
|
|
115
|
+
*not* covered - a real gap rather than a simplification, and one that has to be closed on both sides
|
|
116
|
+
at once.
|
|
117
|
+
|
|
118
|
+
Verification is implemented too, not just signing, and is what the test suite's fake gateway uses:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { SignableRequest, signingSchemeOf } from "euclid-ndk";
|
|
122
|
+
|
|
123
|
+
const request = new SignableRequest("POST", "/")
|
|
124
|
+
.headersFrom(incomingHeaders)
|
|
125
|
+
.setBody(incomingBody)
|
|
126
|
+
.setScheme("https");
|
|
127
|
+
|
|
128
|
+
const scheme = signingSchemeOf(request);
|
|
129
|
+
const keyId = scheme?.verify(request, lookupSecret) ?? null;
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### TLS
|
|
133
|
+
|
|
134
|
+
`https://` URLs are verified against the system trust store. A euclid deployment usually presents
|
|
135
|
+
its own certificate, so `/etc/euclid/euclid_cert.crt` is trusted *alongside* the system store when
|
|
136
|
+
that file exists - the same default `euclid-cli --ca-cert` uses. Node replaces the trust store when
|
|
137
|
+
it is given a CA, so the union is spelled out here rather than assumed. Point `caCertPath` elsewhere,
|
|
138
|
+
or pass `verify(false)` for a development server whose certificate nothing vouches for.
|
|
139
|
+
|
|
140
|
+
### Errors
|
|
141
|
+
|
|
142
|
+
| Error | Thrown when |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `EuclidAuthenticationError` | a login was refused |
|
|
145
|
+
| `EuclidServiceError` | a module refused or failed an action; carries `target`, `action`, `status` and `reason` |
|
|
146
|
+
| `EuclidError` | base class for both |
|
|
147
|
+
|
|
148
|
+
`reason` is the server's own message, pulled out of the `{"error": "..."}` body every euclid module
|
|
149
|
+
answers failures with.
|
|
150
|
+
|
|
151
|
+
### Retries
|
|
152
|
+
|
|
153
|
+
Two, both narrow on purpose:
|
|
154
|
+
|
|
155
|
+
* A request that failed because the connection was closed while it sat idle is sent again once, on a
|
|
156
|
+
fresh connection. Only failures that produced no response at all qualify.
|
|
157
|
+
* A 401 whose body says the credentials had expired is sent again once with rebuilt headers, if
|
|
158
|
+
rebuilding them produces something different. A wrong password or a missing permission is answered
|
|
159
|
+
once, as before.
|
|
160
|
+
|
|
161
|
+
For a process that outlives its token, set `session.tokenProvider` to something that re-reads the
|
|
162
|
+
credentials file; the retry then has a fresh token to use.
|
|
163
|
+
|
|
164
|
+
## What EAM covers
|
|
165
|
+
|
|
166
|
+
| Method | Action |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| `listUsers`, `register`, `deleteUser` | users |
|
|
169
|
+
| `createAccessKey`, `listAccessKeys`, `deleteAccessKey` | the caller's own signing credentials |
|
|
170
|
+
| `createUserGroup`, `listUserGroups`, `deleteUserGroup`, `addUserToUserGroup`, `removeUserFromUserGroup` | groups |
|
|
171
|
+
| `createAccount`, `listAccounts`, `deleteAccount` | accounts |
|
|
172
|
+
| `createNamespace`, `listNamespaces`, `deleteNamespace`, `grantNamespaceAccess`, `revokeNamespaceAccess` | namespaces |
|
|
173
|
+
| `changeNamespace` | which namespace this session is scoped to |
|
|
174
|
+
| `metrics` | EAM's own metrics |
|
|
175
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
176
|
+
|
|
177
|
+
Several of these are administrator-only server-side; `session.isAdmin` says whether the logged-in
|
|
178
|
+
user is one, though the server enforces it regardless.
|
|
179
|
+
|
|
180
|
+
## What ESM covers
|
|
181
|
+
|
|
182
|
+
`session.esm()` answers with the storage client, and with the same one every time - a connection per
|
|
183
|
+
module rather than per call. It follows the session it came from: a `changeNamespace` between two
|
|
184
|
+
calls scopes the second one.
|
|
185
|
+
|
|
186
|
+
| Method | Action |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `createBucket`, `listBuckets`, `getBucketErn`, `getBucketSize`, `renameBucket`, `purgeBucket`, `deleteBucket` | buckets |
|
|
189
|
+
| `addBucketTag`, `setBucketTag`, `deleteBucketTag` | bucket tags |
|
|
190
|
+
| `enableEncryption`, `disableEncryption` | encryption at rest, under an EKM key |
|
|
191
|
+
| `setBucketInternal` | whether a bucket is euclid's own plumbing, and so left out of a listing |
|
|
192
|
+
| `listObjects`, `getObjectCount`, `copyObject`, `moveObject`, `renameObject`, `deleteObject`, `deleteObjects` | objects |
|
|
193
|
+
| `touchObject` | re-announce objects already stored, for a listener that missed their events |
|
|
194
|
+
| `addObjectAttribute`, `setObjectAttribute`, `listObjectAttributes`, `deleteObjectAttribute` | user-defined attributes |
|
|
195
|
+
| `subscribe`, `listSubscriptions`, `unsubscribe`, `parseBucketEvent` | a bucket's events, into a queue or a topic |
|
|
196
|
+
| `putObject`, `getObject`, `uploadFile`, `downloadFile` | the object's bytes themselves |
|
|
197
|
+
| `metrics` | ESM's own metrics |
|
|
198
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
199
|
+
|
|
200
|
+
Buckets and objects are named by ERN, not by name - `createBucket` answers with the one everything
|
|
201
|
+
else takes, and `getBucketErn` is how an existing bucket's is looked up.
|
|
202
|
+
|
|
203
|
+
### Writing and reading bytes
|
|
204
|
+
|
|
205
|
+
`putObject` and `getObject` are one request each; `uploadFile` and `downloadFile` are the multipart
|
|
206
|
+
path, and are what a file of any size wants:
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
await esm.uploadFile(bucket.ern, "data/large.bin", "large.bin", { partSize: 5 * 1024 * 1024, concurrency: 4 });
|
|
210
|
+
const written = await esm.downloadFile(bucket.ern, "data/large.bin", "copy.bin");
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The file is read a part at a time and no more than `concurrency` parts are ever in flight, so what an
|
|
214
|
+
upload costs in memory is bounded by the two together whatever the file's size. A download does not
|
|
215
|
+
know its size before asking, so one request is tried first and the server answering HTTP 413 is what
|
|
216
|
+
says the object needs the parts - a caller does not have to know which of the two an object needs.
|
|
217
|
+
|
|
218
|
+
Each step of a transfer is retried up to four times on a 5xx or a request that got no answer at all,
|
|
219
|
+
including the `create-upload` and `complete-upload` bracketing the parts: those run once per transfer
|
|
220
|
+
rather than once per part, but giving up on a transient failure in one of them discards the whole
|
|
221
|
+
file. A 4xx is answered once, since a repeat would be answered identically.
|
|
222
|
+
|
|
223
|
+
These four actions - `put-object`, `get-object`, `upload-part`, `download-part` - carry the object's
|
|
224
|
+
bytes as the request body, with the bucket, key and part number riding as headers, which is what
|
|
225
|
+
keeps a 5 MiB part 5 MiB on the wire rather than a third larger as base64 inside JSON. They also
|
|
226
|
+
present the session's bearer token rather than a signature, which is what euclid-cli, euclid-jdk and
|
|
227
|
+
euclid-pdk do for the same four, so every client writes objects the same way. A session that asked
|
|
228
|
+
for `AUTH_SIGNATURE` signs them anyway: it asked not to be handed a token silently.
|
|
229
|
+
|
|
230
|
+
### Attributes
|
|
231
|
+
|
|
232
|
+
An object carries two attribute maps, and they are not the same one. `attributes` are the caller's
|
|
233
|
+
own, listed back by `listObjectAttributes` and meaningless to euclid; `systemAttributes` are euclid's
|
|
234
|
+
envelope, which travels with the object across every hop. The one euclid acts on is `priority`.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import { PRIORITY_LOW } from "euclid-ndk";
|
|
238
|
+
|
|
239
|
+
await esm.putObject(bucket.ern, "notes/hello.txt", Buffer.from("hello\n"), {
|
|
240
|
+
attributes: { author: "euclid-ndk", revision: 1 },
|
|
241
|
+
systemAttributes: { priority: PRIORITY_LOW },
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Values are typed on the wire: a plain value is tagged with the type euclid stores it under - a whole
|
|
246
|
+
number as `long`, anything else numeric as `double`, a `Uint8Array` as `binary` and base64 encoded -
|
|
247
|
+
and `{ type: "int", value: 3 }` is how a caller asks for a tag other than the obvious one. `binary`
|
|
248
|
+
comes back as a `Buffer`, so the encoding never reaches the caller.
|
|
249
|
+
|
|
250
|
+
Attributes belong on the write rather than added afterwards: completing an upload is finished off in
|
|
251
|
+
the background from what that call carried, so an attribute added between the two is overwritten.
|
|
252
|
+
|
|
253
|
+
### Subscriptions
|
|
254
|
+
|
|
255
|
+
A subscription announces a bucket's object events to a queue or a topic, filtered by the server as it
|
|
256
|
+
publishes. What lands there is an ordinary message whose body is the event:
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import { OBJECT_CREATED, QUEUE, parseBucketEvent } from "euclid-ndk";
|
|
260
|
+
|
|
261
|
+
await esm.subscribe(bucket.ern, QUEUE, queueErn, { eventTypes: [OBJECT_CREATED], prefix: "2026/" });
|
|
262
|
+
const event = parseBucketEvent(message.body);
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`unsubscribe` takes the subscription's own ERN - not the bucket's, and not the target's. Subscribing
|
|
266
|
+
is not idempotent: a second call delivers every matching event twice, so a caller that may run twice
|
|
267
|
+
checks `listSubscriptions` first.
|
|
268
|
+
|
|
269
|
+
## What EQS covers
|
|
270
|
+
|
|
271
|
+
`session.eqs()` answers with the queue client.
|
|
272
|
+
|
|
273
|
+
| Method | Action |
|
|
274
|
+
| --- | --- |
|
|
275
|
+
| `createQueue`, `listQueues`, `getQueueErn`, `getQueueMetadata`, `purgeQueue`, `purgeAllQueues`, `deleteQueue` | queues |
|
|
276
|
+
| `addQueueTag`, `setQueueTag`, `deleteQueueTag` | queue tags |
|
|
277
|
+
| `stopQueue`, `startQueue`, `setQueueVisibility` | what a queue hands out, and for how long |
|
|
278
|
+
| `sendMessage`, `receiveMessages`, `receiveAllMessages`, `deleteMessage`, `deleteMessageById` | messages |
|
|
279
|
+
| `listMessages`, `getMessageCount`, `getMessageMetadata` | inspecting a queue without consuming it |
|
|
280
|
+
| `getMessageAttribute`, `setMessageAttribute`, `setMessageVisibility` | one message at a time |
|
|
281
|
+
| `redriveDlq` | moving a dead letter queue's messages back where they came from |
|
|
282
|
+
| `asInternal`, `metrics` | euclid's own traffic, and EQS's own metrics |
|
|
283
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
284
|
+
|
|
285
|
+
Receiving is a lease rather than a read. A message a consumer takes is invisible to every other
|
|
286
|
+
consumer until its visibility timeout expires, and deleting it with the receipt handle is what says the
|
|
287
|
+
work was done - so the delete belongs after the work, not before it:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
const eqs = session.eqs();
|
|
291
|
+
const queue = await eqs.createQueue("orders", { visibility: 30, maxRetries: 5, dlqName: "orders-dlq" });
|
|
292
|
+
|
|
293
|
+
await eqs.sendMessage(queue.ern, JSON.stringify({ order: 17 }), { attributes: { tenant: "acme" } });
|
|
294
|
+
|
|
295
|
+
for (const message of (await eqs.receiveMessages(queue.ern, { waitTimeSeconds: 20 })).items) {
|
|
296
|
+
await handle(message.body);
|
|
297
|
+
await eqs.deleteMessage(message.receiptHandle);
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
A consumer that dies instead simply stops holding the lease and the message comes back; after
|
|
302
|
+
`maxRetries` deliveries it goes to the dead letter queue, where `getMessageMetadata` explains why.
|
|
303
|
+
|
|
304
|
+
`waitTimeSeconds` is a long poll, and the waiting is the server's: it holds the request open until a
|
|
305
|
+
message lands or the window runs out, so an idle queue costs one request for the whole window rather
|
|
306
|
+
than one per tick, and the request gets a timeout of its own rather than the session's. Two details are
|
|
307
|
+
the client's:
|
|
308
|
+
|
|
309
|
+
* With no wait asked for, the queue's depth is checked first - a receive is a write, and one that takes
|
|
310
|
+
nothing is work the server did for nothing.
|
|
311
|
+
* The server keeps a bounded number of long-poll slots so that waiting consumers cannot starve the
|
|
312
|
+
producers sending to them. With none free it answers at once, which comes back empty with time still
|
|
313
|
+
on the clock; the client then pauses briefly and asks again for what is left of the window, rather
|
|
314
|
+
than hammering a server that is already short of threads.
|
|
315
|
+
|
|
316
|
+
`asInternal()` marks a client's requests as euclid's own traffic. The same `get-message-count` is a
|
|
317
|
+
user's question one moment and a metric collector's poll the next, and only the caller knows which, so
|
|
318
|
+
instrumentation says so rather than leaving the server to guess from a rate.
|
|
319
|
+
|
|
320
|
+
## What ENS covers
|
|
321
|
+
|
|
322
|
+
`session.ens()` answers with the notification client.
|
|
323
|
+
|
|
324
|
+
| Method | Action |
|
|
325
|
+
| --- | --- |
|
|
326
|
+
| `createTopic`, `listTopics`, `getTopicErn`, `getTopicMetadata`, `purgeTopic`, `purgeAllTopics`, `deleteTopic` | topics |
|
|
327
|
+
| `addTopicTag`, `setTopicTag`, `deleteTopicTag` | topic tags |
|
|
328
|
+
| `publishMessage`, `listMessages`, `getMessageCount` | messages |
|
|
329
|
+
| `getMessageAttribute`, `setMessageAttribute` | one published message at a time |
|
|
330
|
+
| `subscribe`, `listSubscriptions`, `unsubscribe` | delivery onward to a queue |
|
|
331
|
+
| `metrics` | ENS's own metrics |
|
|
332
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
333
|
+
|
|
334
|
+
What a topic does with a message is the whole difference from a queue: a queue holds one until a
|
|
335
|
+
consumer takes it, a topic hands each one to every subscriber and keeps it as a record of having done
|
|
336
|
+
so. There is no receive here and no receipt handle - a subscriber consumes from its own queue:
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
await ens.subscribe(topic.ern, await eqs.getQueueErn("orders"));
|
|
340
|
+
await ens.publishMessage(topic.ern, JSON.stringify({ order: 17 }), { priority: PRIORITY_HIGH });
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Each subscriber consumes independently, so one that is slow or stopped delays nobody else. A message
|
|
344
|
+
published before a subscription existed is not delivered retrospectively, and one already delivered is
|
|
345
|
+
not withdrawn when the subscription goes. Subscribing is not idempotent, as in ESM.
|
|
346
|
+
|
|
347
|
+
A topic's counters are not a queue's: `available`, `send` and `resend` count delivery rather than a
|
|
348
|
+
backlog, since a topic does not hold one.
|
|
349
|
+
|
|
350
|
+
One wire asymmetry is reproduced rather than papered over: an attribute's name travels as `name` in
|
|
351
|
+
most of EQS and as `key` throughout ENS, so a request this SDK builds matches what euclid-cli and
|
|
352
|
+
euclid-jdk send.
|
|
353
|
+
|
|
354
|
+
## What EKM covers
|
|
355
|
+
|
|
356
|
+
`session.ekm()` answers with the key client.
|
|
357
|
+
|
|
358
|
+
| Method | Action |
|
|
359
|
+
| --- | --- |
|
|
360
|
+
| `createKey`, `listKeys`, `setKeyDescription`, `addKeyTag`, `deleteKeyTag` | keys |
|
|
361
|
+
| `revokeKey`, `deleteKey` | taking a key out of use, and out of existence |
|
|
362
|
+
| `encrypt`, `decrypt` | using one |
|
|
363
|
+
| `importCertificate`, `createCertificate`, `getCertificate`, `listCertificates`, `deleteCertificate` | the certificates a deployment serves |
|
|
364
|
+
| `metrics` | EKM's own metrics |
|
|
365
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
366
|
+
|
|
367
|
+
Key material never leaves the server: the bytes go to the key rather than the key coming to the bytes.
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
const ekm = session.ekm();
|
|
371
|
+
const key = await ekm.createKey({ description: "customer exports" });
|
|
372
|
+
|
|
373
|
+
const sealed = await ekm.encrypt(key.name, "account 4711"); // IV || ciphertext || tag
|
|
374
|
+
const plain = await ekm.decrypt(key.name, sealed);
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
A key is named two ways and they are not interchangeable: `name` is the ID the server minted, and is what
|
|
378
|
+
encrypts, decrypts and is deleted; the ERN is what revokes, describes and tags. Both are on every key a
|
|
379
|
+
listing returns, and nothing a listing returns is material.
|
|
380
|
+
|
|
381
|
+
Revoking and deleting are different in the way that matters. A revoked key encrypts nothing further and
|
|
382
|
+
still decrypts what it wrote; `deleteKey` schedules a date - seven days out by default - after which
|
|
383
|
+
everything it encrypted is unreadable. That window is the only chance anybody gets to notice, which is
|
|
384
|
+
why it is a date rather than an act, and a key inside it still decrypts.
|
|
385
|
+
|
|
386
|
+
`encrypt` and `decrypt` carry raw bytes and present the session's bearer token, exactly as ESM's transfer
|
|
387
|
+
actions do, and for the same reasons - including that a session which asked for `AUTH_SIGNATURE` signs
|
|
388
|
+
them anyway. A `description` is worth supplying at creation: a key outlives the reason it was made, and
|
|
389
|
+
months later it is the only thing that answers whether the key can be deleted.
|
|
390
|
+
|
|
391
|
+
## What EKV covers
|
|
392
|
+
|
|
393
|
+
`session.ekv()` answers with the key-value store.
|
|
394
|
+
|
|
395
|
+
| Method | Action |
|
|
396
|
+
| --- | --- |
|
|
397
|
+
| `createTable`, `describeTable`, `listTables`, `deleteTable` | tables |
|
|
398
|
+
| `putItem`, `getItem`, `findItem`, `deleteItem` | one item at a time |
|
|
399
|
+
| `query` | the items of one partition, in sort-key order |
|
|
400
|
+
| `scan` | a table's items without regard to their key |
|
|
401
|
+
| `metrics` | EKV's own metrics |
|
|
402
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
403
|
+
|
|
404
|
+
A table is keyed on one attribute or on two: a partition key that identifies an item, and optionally a sort
|
|
405
|
+
key that orders the items sharing a partition key - which is what makes a partition readable as a range.
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
import { KEY_NUMBER, SORT_GE } from "euclid-ndk";
|
|
409
|
+
|
|
410
|
+
const ekv = session.ekv();
|
|
411
|
+
await ekv.createTable("sessions", "userId", { sortKey: "startedAt", sortKeyType: KEY_NUMBER });
|
|
412
|
+
|
|
413
|
+
await ekv.putItem("sessions", { userId: "jens", startedAt: 1757462400, host: "laptop" });
|
|
414
|
+
const recent = await ekv.query("sessions", "jens", { sortOperator: SORT_GE, sortValue: 1757462400 });
|
|
415
|
+
for (const item of recent.items) console.log(item.attributes.host);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
The key types are what make a range mean what it should: a `KEY_NUMBER` sort key orders 2, 9, 10, 100 rather
|
|
419
|
+
than putting "10" before "9". They cannot be changed after the table is created. Everything else about an
|
|
420
|
+
item is a free-form document - scalars, arrays, nested objects - and is *not* the typed `Variant` that EQS,
|
|
421
|
+
ENS and ESM attributes use: EKV stores what JSON can express.
|
|
422
|
+
|
|
423
|
+
`putItem` replaces rather than merges, so changing one field means reading the item, changing it and writing
|
|
424
|
+
the whole thing back. That is why `Item` keeps the server's `_created` and `_modified` out of `attributes`:
|
|
425
|
+
left in, they would be written back as two attributes of the caller's own, and they would stick.
|
|
426
|
+
|
|
427
|
+
`getItem` throws on a miss, because "there is no such item" and "here is an item with nothing in it" are
|
|
428
|
+
different answers and a caller should not have to tell them apart; `findItem` is the same read answering
|
|
429
|
+
`null` instead, and only for a 404. `query` is the lookup EKV is for - it addresses a partition by key -
|
|
430
|
+
while `scan` reads the table, which is right for an export and wrong for a lookup.
|
|
431
|
+
|
|
432
|
+
## What EAP covers
|
|
433
|
+
|
|
434
|
+
`session.eap()` answers with the application client. Every action is administrator-only server-side;
|
|
435
|
+
`session.isAdmin` says whether the logged-in user is one.
|
|
436
|
+
|
|
437
|
+
| Method | Action |
|
|
438
|
+
| --- | --- |
|
|
439
|
+
| `createApplication`, `updateApplication`, `redeployApplication`, `deleteApplication` | deploying |
|
|
440
|
+
| `startApplication`, `stopApplication`, `listApplications`, `getApplication` | running |
|
|
441
|
+
| `setLogLevel`, `resetLogLevel` | what one application logs, without restarting it |
|
|
442
|
+
| `metrics` | EAP's own metrics |
|
|
443
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
444
|
+
|
|
445
|
+
An application is deployed from an artifact already in a bucket - ESM puts it there, EAP names it:
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
import { RUNTIME_JAVA } from "euclid-ndk";
|
|
449
|
+
|
|
450
|
+
await esm.uploadFile(bucketErn, "order-service-1.4.0.jar", "target/order-service.jar");
|
|
451
|
+
await eap.createApplication("order-service", RUNTIME_JAVA, "artifacts", "order-service-1.4.0.jar", {
|
|
452
|
+
queues: ["orders"],
|
|
453
|
+
minInstances: 2,
|
|
454
|
+
maxInstances: 5,
|
|
455
|
+
});
|
|
456
|
+
await eap.startApplication("order-service");
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
The deployment says which buckets and queues the application may reach, and euclid grants those to the
|
|
460
|
+
identity it runs as: a technical principal it creates unless one is named, with no password, no login and
|
|
461
|
+
one access key, so that nothing an application leaks is a person's credential.
|
|
462
|
+
|
|
463
|
+
Starting asks rather than waits. `desiredState` is what somebody asked for and `state` is what is
|
|
464
|
+
actually answering, so a freshly started application usually comes back `RUNNING`/`STOPPED` - the two
|
|
465
|
+
differing is an application starting up, and the two differing for long is one that cannot.
|
|
466
|
+
|
|
467
|
+
`updateApplication` sends only the fields it is given, because that is the distinction the server draws,
|
|
468
|
+
as in ESS and EAG below:
|
|
469
|
+
leaving `command` out keeps the stored command, while passing `""` clears it and hands the artifact back
|
|
470
|
+
to the runtime's own interpreter. `buckets` and `queues` are re-resolved together whenever either is
|
|
471
|
+
named - so pass both or neither, since naming one revokes what the other granted. For a new build of the
|
|
472
|
+
same application, `redeployApplication` is the call; one that would change neither the version nor the
|
|
473
|
+
checksum is refused, which usually means the new artifact never reached the bucket.
|
|
474
|
+
|
|
475
|
+
## What ESS covers
|
|
476
|
+
|
|
477
|
+
`session.ess()` answers with the secret store.
|
|
478
|
+
|
|
479
|
+
| Method | Action |
|
|
480
|
+
| --- | --- |
|
|
481
|
+
| `createSecret`, `getSecret`, `listSecrets`, `deleteSecret` | secrets |
|
|
482
|
+
| `rotateSecret`, `updateSecret` | replacing a value, a description, or the key it is under |
|
|
483
|
+
| `addSecretTag`, `deleteSecretTag` | tags |
|
|
484
|
+
| `metrics` | ESS's own metrics |
|
|
485
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
486
|
+
|
|
487
|
+
A value is encrypted with EKM before it is stored, so a secret's life is tied to a key's: deleting that key
|
|
488
|
+
there is what makes the value unrecoverable, whatever ESS still says about it.
|
|
489
|
+
|
|
490
|
+
```ts
|
|
491
|
+
const ess = session.ess();
|
|
492
|
+
await ess.createSecret("db-password", "hunter2", { description: "the reporting database" });
|
|
493
|
+
|
|
494
|
+
const password = (await ess.getSecret("db-password")).value;
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
`getSecret` is the only call that answers with a value, and so the only point at which one enters the
|
|
498
|
+
process - everything else answers with metadata alone, so a listing, a rotation and a tag change can be
|
|
499
|
+
logged and printed without being the thing that leaks it. `rotateSecret` is `updateSecret` with a value:
|
|
500
|
+
what bumps `version` and sets `rotated`, which together are what an audit of "has this been rotated since
|
|
501
|
+
the incident" actually reads.
|
|
502
|
+
|
|
503
|
+
`updateSecret` sends only what it names, with the same empty-string rule as EAP: leaving `description` out
|
|
504
|
+
keeps the stored one, `""` clears it, and an empty `value` stores an empty value because that is a value
|
|
505
|
+
somebody may legitimately have. Naming a `keyErn` re-encrypts the value under that key, which is how a
|
|
506
|
+
secret is moved off a key that is being retired.
|
|
507
|
+
|
|
508
|
+
## What EAG covers
|
|
509
|
+
|
|
510
|
+
`session.eag()` answers with the API gateway client. Every action is administrator-only server-side.
|
|
511
|
+
|
|
512
|
+
| Method | Action |
|
|
513
|
+
| --- | --- |
|
|
514
|
+
| `createRoute`, `createModuleRoute`, `updateRoute`, `getRoute`, `listRoutes`, `deleteRoute` | published paths |
|
|
515
|
+
| `setRouteActive` | taking one out of service, and putting it back |
|
|
516
|
+
| `listListeners` | the ports the gateway answers on, and whether it is answering |
|
|
517
|
+
| `metrics` | EAG's own metrics |
|
|
518
|
+
| `call(action, payload)` | anything the server gained that this SDK has not wrapped yet |
|
|
519
|
+
|
|
520
|
+
A route publishes a path prefix and says where everything beneath it goes: to an application euclid runs,
|
|
521
|
+
or to one action of a euclid module. It is one or the other - never both, never neither - and this client
|
|
522
|
+
refuses the other two before the round trip:
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
import { ROUTE_AUTH_EUCLID } from "euclid-ndk";
|
|
526
|
+
|
|
527
|
+
await eag.createRoute("orders", "/api/orders", {
|
|
528
|
+
applicationId: "order-service",
|
|
529
|
+
methods: ["GET", "POST"], // none means every method
|
|
530
|
+
authentication: ROUTE_AUTH_EUCLID, // or ROUTE_AUTH_NONE, or ROUTE_AUTH_BASIC
|
|
531
|
+
});
|
|
532
|
+
await eag.createModuleRoute("login", "/euclid/login", "eam", "login");
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
Module routes are the way in for something outside euclid that needs euclid itself - a browser that has to
|
|
536
|
+
log in before it can call anything. Without one, a front end would talk to the API gateway for the
|
|
537
|
+
application and to euclid's own gateway for its credentials: two ports, two origins, and CORS between them.
|
|
538
|
+
|
|
539
|
+
`namespace` and `region` are left out of the request entirely unless named, because this server reads an
|
|
540
|
+
empty namespace as *the empty namespace* rather than as "unspecified" - sending one would scope the route
|
|
541
|
+
to nothing. `setRouteActive` is how something stops being exposed in a hurry: the route stays exactly as it
|
|
542
|
+
was and comes back the same, which deleting and recreating it would not guarantee.
|
|
543
|
+
|
|
544
|
+
`listListeners` answers with a page of listeners plus `serving`, which says whether the gateway's ports are
|
|
545
|
+
bound at all. A listener whose port was taken, or whose certificate could not be loaded, is still listed -
|
|
546
|
+
it is the one somebody is looking for. An HTTPS listener's certificate arrives flat, as a dozen
|
|
547
|
+
`certificate*` fields, and is gathered back into one `certificate` object here; it is `null` for a plain
|
|
548
|
+
HTTP listener and for an HTTPS one the server found none for.
|
|
549
|
+
|
|
550
|
+
## Development
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
npm install
|
|
554
|
+
npm test # compiles src and test into build/, then runs node --test
|
|
555
|
+
npm run build # compiles src into dist/ with its .d.ts, which is what npm publishes
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
The suite runs against a fake euclid gateway (`test/fake-gateway.ts`) that authenticates requests
|
|
559
|
+
with the same rules `Core::HttpActionServer::Authenticate` applies, so a client that signs one thing
|
|
560
|
+
and sends another fails there rather than in production. The SigV4 canonicalisation is additionally
|
|
561
|
+
pinned by AWS's own published test vectors, which is what makes this SDK, euclid's C++, euclid-jdk
|
|
562
|
+
and euclid-pdk agree rather than merely each agree with itself.
|
|
563
|
+
|
|
564
|
+
Behind that gateway sit two stand-ins that implement rather than stub the parts a client can get
|
|
565
|
+
wrong. `test/fake-storage.ts` assembles what it is sent: an upload's parts are joined in part order and
|
|
566
|
+
a download hands back the byte range asked for, so a client that numbers its parts wrongly, sizes them
|
|
567
|
+
inconsistently or reassembles them out of order fails there rather than by writing a corrupt object to
|
|
568
|
+
a real server. `test/fake-queues.ts` really leases messages out and either honours a long poll or
|
|
569
|
+
declines it, which is what makes "took a message twice" and "abandoned a poll the server was still
|
|
570
|
+
serving" visible to a test at all.
|
|
571
|
+
|
|
572
|
+
Tests are compiled rather than run through a loader or node's type stripping, so `npm test` behaves
|
|
573
|
+
the same on every supported node.
|
|
574
|
+
|
|
575
|
+
### Releasing
|
|
576
|
+
|
|
577
|
+
A release says its version in three places, and `.github/workflows/publish.yml` refuses to publish
|
|
578
|
+
unless all three agree: the tag, `version` in `package.json` (which names the tarball), and `VERSION`
|
|
579
|
+
in `src/index.ts` (which is what an application asking this SDK its own version is told - checked
|
|
580
|
+
against the built `dist`, since that is what ships). So bump the two files, then tag and push:
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
npm version 0.2.0 --no-git-tag-version # package.json
|
|
584
|
+
$EDITOR src/index.ts # VERSION
|
|
585
|
+
git commit -am "chore: release 0.2.0"
|
|
586
|
+
git tag -a v0.2.0 -m "euclid-ndk 0.2.0"
|
|
587
|
+
git push origin main v0.2.0
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
The tag runs the tests again (the test workflow triggers on branches, so a tag push would otherwise
|
|
591
|
+
run nothing), checks the three versions, builds `dist`, packs the tarball and publishes it to npm with
|
|
592
|
+
a provenance attestation - a signed statement of which workflow, at which commit, built what was
|
|
593
|
+
published. `workflow_dispatch` does the same for whatever `main` says, which is what a version whose
|
|
594
|
+
tag predates this workflow needs; it checks the two files against each other but has no tag to
|
|
595
|
+
compare.
|
|
596
|
+
|
|
597
|
+
Publishing authenticates with an `NPM_TOKEN` secret. Once the package exists on npm, configuring a
|
|
598
|
+
trusted publisher for this repository and `publish.yml` replaces it: npm then mints a short-lived
|
|
599
|
+
token from the workflow's own OIDC identity, and the secret can be deleted.
|
|
600
|
+
|
|
601
|
+
## Licence
|
|
602
|
+
|
|
603
|
+
Apache License 2.0.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request signing and verification for euclid.
|
|
3
|
+
*
|
|
4
|
+
* Two schemes, one credential pair. {@link import("./sigv4.js")} is what euclid has always spoken;
|
|
5
|
+
* {@link import("./rfc9421.js")} is the standard scheme replacing it. Both are keyed by the access
|
|
6
|
+
* key ID and secret a login hands back, and {@link SigningScheme} is how a client picks between
|
|
7
|
+
* them.
|
|
8
|
+
*
|
|
9
|
+
* Verification is here as well as signing, because it is the only way to demonstrate that the two
|
|
10
|
+
* canonicalisations are the same one, and because a Node service fronting euclid needs to check the
|
|
11
|
+
* signatures it receives with the same rules the server applies.
|
|
12
|
+
*/
|
|
13
|
+
export * as rfc9421 from "./rfc9421.js";
|
|
14
|
+
export { RFC9421, SIGV4, signingSchemeOf } from "./scheme.js";
|
|
15
|
+
export type { SigningScheme } from "./scheme.js";
|
|
16
|
+
export { SignableRequest } from "./signable.js";
|
|
17
|
+
export * as sigv4 from "./sigv4.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9D,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request signing and verification for euclid.
|
|
3
|
+
*
|
|
4
|
+
* Two schemes, one credential pair. {@link import("./sigv4.js")} is what euclid has always spoken;
|
|
5
|
+
* {@link import("./rfc9421.js")} is the standard scheme replacing it. Both are keyed by the access
|
|
6
|
+
* key ID and secret a login hands back, and {@link SigningScheme} is how a client picks between
|
|
7
|
+
* them.
|
|
8
|
+
*
|
|
9
|
+
* Verification is here as well as signing, because it is the only way to demonstrate that the two
|
|
10
|
+
* canonicalisations are the same one, and because a Node service fronting euclid needs to check the
|
|
11
|
+
* signatures it receives with the same rules the server applies.
|
|
12
|
+
*/
|
|
13
|
+
export * as rfc9421 from "./rfc9421.js";
|
|
14
|
+
export { RFC9421, SIGV4, signingSchemeOf } from "./scheme.js";
|
|
15
|
+
export { SignableRequest } from "./signable.js";
|
|
16
|
+
export * as sigv4 from "./sigv4.js";
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC"}
|