@nacre.work/core 0.0.0 → 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 +202 -0
- package/NOTICE +9 -0
- package/dist/audit.d.ts +41 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +18 -0
- package/dist/audit.js.map +1 -0
- package/dist/authz/cache.d.ts +20 -0
- package/dist/authz/cache.d.ts.map +1 -1
- package/dist/authz/cache.js +29 -0
- package/dist/authz/cache.js.map +1 -1
- package/dist/authz/filter.d.ts +54 -3
- package/dist/authz/filter.d.ts.map +1 -1
- package/dist/authz/filter.js +45 -9
- package/dist/authz/filter.js.map +1 -1
- package/dist/authz/index.d.ts +3 -3
- package/dist/authz/index.d.ts.map +1 -1
- package/dist/authz/index.js +2 -2
- package/dist/authz/index.js.map +1 -1
- package/dist/config.d.ts +55 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +245 -7
- package/dist/config.js.map +1 -1
- package/dist/db/client.d.ts +39 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +82 -0
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +49 -0
- package/dist/db/migrate.js.map +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.d.ts +57 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +84 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/logging.d.ts +46 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +110 -0
- package/dist/logging.js.map +1 -0
- package/dist/metadata.d.ts +50 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +127 -0
- package/dist/metadata.js.map +1 -0
- package/dist/metrics.d.ts +19 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +84 -2
- package/dist/metrics.js.map +1 -1
- package/dist/migrations/0008_authenticating_lookup.sql +124 -0
- package/dist/migrations/0009_refresh_tokens.sql +73 -0
- package/dist/migrations/0010_definer_search_path.sql +22 -0
- package/dist/migrations/0011_sweep_lease.sql +36 -0
- package/dist/migrations/0012_prune.sql +118 -0
- package/dist/migrations/0013_reindex.sql +65 -0
- package/dist/migrations/0014_retired_collections.sql +68 -0
- package/dist/oauth.d.ts +45 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +53 -0
- package/dist/oauth.js.map +1 -0
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +100 -13
- package/dist/observability.js.map +1 -1
- package/dist/passwords.d.ts +37 -0
- package/dist/passwords.d.ts.map +1 -0
- package/dist/passwords.js +234 -0
- package/dist/passwords.js.map +1 -0
- package/dist/redis.d.ts +46 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +234 -0
- package/dist/redis.js.map +1 -0
- package/dist/reindex.d.ts +87 -0
- package/dist/reindex.d.ts.map +1 -0
- package/dist/reindex.js +97 -0
- package/dist/reindex.js.map +1 -0
- package/dist/s3.d.ts +111 -0
- package/dist/s3.d.ts.map +1 -0
- package/dist/s3.js +242 -0
- package/dist/s3.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/vector/query.d.ts +49 -4
- package/dist/vector/query.d.ts.map +1 -1
- package/dist/vector/query.js +43 -10
- package/dist/vector/query.js.map +1 -1
- package/dist/vector/search.d.ts +175 -4
- package/dist/vector/search.d.ts.map +1 -1
- package/dist/vector/search.js +320 -7
- package/dist/vector/search.js.map +1 -1
- package/package.json +26 -8
package/dist/reindex.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The state of a layer's move to a different embedding model.
|
|
3
|
+
*
|
|
4
|
+
* Shared between the API, which starts one and reports on it, and the worker,
|
|
5
|
+
* which does the work — so the shape is defined once rather than agreed twice.
|
|
6
|
+
*
|
|
7
|
+
* ## What is built, and what is not
|
|
8
|
+
*
|
|
9
|
+
* `docs/architecture.md` specifies six steps. Four are here: add the new named
|
|
10
|
+
* vector, reindex existing documents in bounded batches, switch `vector_name`
|
|
11
|
+
* atomically, keep search available throughout.
|
|
12
|
+
*
|
|
13
|
+
* Two are not, and are named rather than quietly skipped:
|
|
14
|
+
*
|
|
15
|
+
* - **The Recall@10 gate.** Step 4 checks the new index against "the layer's
|
|
16
|
+
* reference query set" before switching. There is no reference query set
|
|
17
|
+
* anywhere in this product — not a table, not an endpoint, not a
|
|
18
|
+
* configuration file — so building the gate would mean inventing the surface
|
|
19
|
+
* it reads from, and a gate that checks against an empty set is a gate that
|
|
20
|
+
* passes. Until it exists, a switch is a decision the operator makes by
|
|
21
|
+
* starting the reindex, and `docs/architecture.md` says so.
|
|
22
|
+
* - **Dropping the old vector after a rollback window.** Step 6. The old vector
|
|
23
|
+
* stays. It costs storage and it is what makes a rollback a one-statement
|
|
24
|
+
* change, which is worth more than the space while this path is new.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* How far along, as a ratio, for `nacre_reindex_progress_ratio`.
|
|
28
|
+
*
|
|
29
|
+
* Clamped at 1 rather than allowed past it. `total` is counted once when the
|
|
30
|
+
* reindex starts and documents ingested afterwards are picked up by the same
|
|
31
|
+
* pass, so `done` legitimately overshoots — and a progress gauge reading 1.4
|
|
32
|
+
* makes an operator think the number is broken rather than that more work
|
|
33
|
+
* arrived. A reindex that is complete reads 1 whatever the counts say.
|
|
34
|
+
*
|
|
35
|
+
* Zero documents is 1, not 0. An empty layer is finished, and a gauge that sits
|
|
36
|
+
* at zero forever for a layer with nothing in it is the stuck-alert shape.
|
|
37
|
+
*/
|
|
38
|
+
export function reindexProgress(state) {
|
|
39
|
+
if (state.status === 'complete')
|
|
40
|
+
return 1;
|
|
41
|
+
// The copy has no per-document measure — it moves points, and how many an
|
|
42
|
+
// organization has is not the layer's `total`. Reporting zero through it is
|
|
43
|
+
// honest: nothing this layer is waiting for has been done yet.
|
|
44
|
+
if (state.phase === 'copying')
|
|
45
|
+
return 0;
|
|
46
|
+
if (state.total <= 0)
|
|
47
|
+
return 1;
|
|
48
|
+
return Math.min(1, state.done / state.total);
|
|
49
|
+
}
|
|
50
|
+
/** The JSON shape stored in the column. Snake case, like every other jsonb here. */
|
|
51
|
+
export function toStateJson(state) {
|
|
52
|
+
return {
|
|
53
|
+
status: state.status,
|
|
54
|
+
phase: state.phase,
|
|
55
|
+
shadow_vector: state.shadowVector,
|
|
56
|
+
provider_id: state.providerId,
|
|
57
|
+
started_at: state.startedAt,
|
|
58
|
+
...(state.finishedAt === undefined ? {} : { finished_at: state.finishedAt }),
|
|
59
|
+
total: state.total,
|
|
60
|
+
done: state.done,
|
|
61
|
+
failed: state.failed,
|
|
62
|
+
...(state.error === undefined ? {} : { error: state.error }),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Read it back, or `undefined` for a column that holds nothing usable.
|
|
67
|
+
*
|
|
68
|
+
* Undefined rather than a thrown error or a default: a layer with no reindex
|
|
69
|
+
* and a layer whose state cannot be parsed both mean "there is nothing to
|
|
70
|
+
* report", and the caller has no different action to take. The check constraint
|
|
71
|
+
* added in `0013` is what makes the second case not happen.
|
|
72
|
+
*/
|
|
73
|
+
export function fromStateJson(raw) {
|
|
74
|
+
if (raw === null || typeof raw !== 'object')
|
|
75
|
+
return undefined;
|
|
76
|
+
const o = raw;
|
|
77
|
+
const status = o.status;
|
|
78
|
+
if (status !== 'running' && status !== 'complete' && status !== 'failed')
|
|
79
|
+
return undefined;
|
|
80
|
+
if (typeof o.shadow_vector !== 'string')
|
|
81
|
+
return undefined;
|
|
82
|
+
return {
|
|
83
|
+
status,
|
|
84
|
+
// Defaulted rather than refused: a state written before the phase existed
|
|
85
|
+
// is an embedding-phase state, because the copy is what introduced it.
|
|
86
|
+
phase: o.phase === 'copying' ? 'copying' : 'embedding',
|
|
87
|
+
shadowVector: o.shadow_vector,
|
|
88
|
+
providerId: typeof o.provider_id === 'string' ? o.provider_id : '',
|
|
89
|
+
startedAt: typeof o.started_at === 'string' ? o.started_at : '',
|
|
90
|
+
...(typeof o.finished_at === 'string' ? { finishedAt: o.finished_at } : {}),
|
|
91
|
+
total: typeof o.total === 'number' ? o.total : 0,
|
|
92
|
+
done: typeof o.done === 'number' ? o.done : 0,
|
|
93
|
+
failed: typeof o.failed === 'number' ? o.failed : 0,
|
|
94
|
+
...(typeof o.error === 'string' ? { error: o.error } : {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=reindex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reindex.js","sourceRoot":"","sources":["../reindex.ts"],"names":[],"mappings":"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"}
|
package/dist/s3.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object storage, signed by hand.
|
|
3
|
+
*
|
|
4
|
+
* `NACRE_S3_*` has been in `docs/config.md` since before there was a server and
|
|
5
|
+
* read by nothing — not used, not validated, so a wrong endpoint or a missing
|
|
6
|
+
* credential was silent while the `full` profile started a MinIO nobody talked
|
|
7
|
+
* to. This is the client that ends that.
|
|
8
|
+
*
|
|
9
|
+
* ─── why not the AWS SDK ───
|
|
10
|
+
*
|
|
11
|
+
* Four operations are needed: put, get, delete, head. `@aws-sdk/client-s3` is
|
|
12
|
+
* tens of megabytes and hundreds of transitive packages, in a container whose
|
|
13
|
+
* job is to read documents other people send it — which is the argument
|
|
14
|
+
* `metrics.ts` already makes for not taking prom-client. SigV4 is a documented
|
|
15
|
+
* algorithm with a published set of test vectors, it fits in this file, and it
|
|
16
|
+
* has no release cadence to track.
|
|
17
|
+
*
|
|
18
|
+
* The rule that comes with that choice: **anything here is verified against a
|
|
19
|
+
* real MinIO before it is believed**, because a signing bug produces a 403 that
|
|
20
|
+
* says nothing about which of the six inputs was wrong.
|
|
21
|
+
*
|
|
22
|
+
* ─── what is deliberately not here ───
|
|
23
|
+
*
|
|
24
|
+
* No multipart upload, so an object is one PUT and `NACRE_MAX_DOCUMENT_BYTES`
|
|
25
|
+
* bounds it. No listing, because nothing needs to enumerate a bucket and a
|
|
26
|
+
* paginated list is where this file would stop being small. No retries: the
|
|
27
|
+
* callers are the ingest queue and the collector, both of which already retry
|
|
28
|
+
* whole units of work and would otherwise retry twice.
|
|
29
|
+
*/
|
|
30
|
+
export interface S3Options {
|
|
31
|
+
/** Including the scheme and port, e.g. `http://minio:9000`. No trailing slash. */
|
|
32
|
+
readonly endpoint: string;
|
|
33
|
+
readonly bucket: string;
|
|
34
|
+
readonly region: string;
|
|
35
|
+
readonly accessKey: string;
|
|
36
|
+
readonly secretKey: string;
|
|
37
|
+
/**
|
|
38
|
+
* `{endpoint}/{bucket}/{key}` rather than `{bucket}.{endpoint}/{key}`.
|
|
39
|
+
*
|
|
40
|
+
* True for MinIO and for anything reached by IP or through a name that is not
|
|
41
|
+
* a wildcard DNS entry, which is every self-hosted deployment. AWS itself
|
|
42
|
+
* wants it false.
|
|
43
|
+
*/
|
|
44
|
+
readonly forcePathStyle: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare class S3Error extends Error {
|
|
47
|
+
readonly status: number;
|
|
48
|
+
readonly method: string;
|
|
49
|
+
readonly key: string;
|
|
50
|
+
constructor(status: number, method: string, key: string, body: string);
|
|
51
|
+
}
|
|
52
|
+
export declare class S3 {
|
|
53
|
+
#private;
|
|
54
|
+
constructor(options: S3Options);
|
|
55
|
+
get bucket(): string;
|
|
56
|
+
/** Store an object, overwriting whatever was there. */
|
|
57
|
+
put(key: string, body: Uint8Array, contentType?: string): Promise<void>;
|
|
58
|
+
/** Fetch an object. `undefined` when it is not there, which is not an error. */
|
|
59
|
+
get(key: string): Promise<Uint8Array | undefined>;
|
|
60
|
+
/**
|
|
61
|
+
* Remove an object. Absent is success.
|
|
62
|
+
*
|
|
63
|
+
* S3 answers `204` whether or not the key existed, and the caller — the
|
|
64
|
+
* collector, purging a deleted document — must be able to run twice.
|
|
65
|
+
*/
|
|
66
|
+
remove(key: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* A URL that fetches one object, for a while, without a credential.
|
|
69
|
+
*
|
|
70
|
+
* Query-string SigV4 rather than the header form: the point is a link a
|
|
71
|
+
* client can follow, so everything that authenticates it has to be in the
|
|
72
|
+
* URL. `host` is the only signed header, and the payload is unsigned because
|
|
73
|
+
* a GET has none.
|
|
74
|
+
*
|
|
75
|
+
* **This is a bearer capability and it leaves the permission model behind.**
|
|
76
|
+
* Nacre checks `read` once, when the link is minted, and the object store
|
|
77
|
+
* checks nothing afterwards — a revocation during the window does not reach a
|
|
78
|
+
* URL already handed out, and neither does a delete. That is what presigning
|
|
79
|
+
* is, in every implementation of it, and it is why the lifetime is a
|
|
80
|
+
* configured number rather than a constant: `NACRE_PRESIGN_TTL` is how long a
|
|
81
|
+
* deployment is willing for that to be true.
|
|
82
|
+
*
|
|
83
|
+
* It is also why the caller of this decides, not this: minting one per search
|
|
84
|
+
* hit would issue ten capabilities to answer a question about relevance, most
|
|
85
|
+
* never followed. See the note on the search response.
|
|
86
|
+
*/
|
|
87
|
+
presign(key: string, ttlSeconds: number): string;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the bucket answers, for readiness.
|
|
90
|
+
*
|
|
91
|
+
* A HEAD on the bucket rather than on an object: it needs no key to exist and
|
|
92
|
+
* it fails for the three reasons that matter — unreachable, wrong bucket,
|
|
93
|
+
* wrong credential. A TCP connect would pass all three.
|
|
94
|
+
*/
|
|
95
|
+
ready(): Promise<boolean>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Where a document's bytes live.
|
|
99
|
+
*
|
|
100
|
+
* Derived from identity — organization, layer, external id — and never from the
|
|
101
|
+
* content. Content addressing would let two documents share an object, and then
|
|
102
|
+
* deleting one would either strand the other or need a reference count that
|
|
103
|
+
* nothing else in this system has. One document, one object, and a re-ingest
|
|
104
|
+
* overwrites in place.
|
|
105
|
+
*
|
|
106
|
+
* The external id is hashed rather than escaped: it is caller-chosen text that
|
|
107
|
+
* may contain anything, and a key derived by escaping is a key whose length and
|
|
108
|
+
* shape a caller controls.
|
|
109
|
+
*/
|
|
110
|
+
export declare function documentKey(orgId: string, layerId: string, externalId: string): string;
|
|
111
|
+
//# sourceMappingURL=s3.d.ts.map
|
package/dist/s3.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../s3.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,WAAW,SAAS;IACxB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;CACjC;AA8BD,qBAAa,OAAQ,SAAQ,KAAK;IAE9B,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM;gBAFX,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM;CAQf;AAED,qBAAa,EAAE;;gBAGD,OAAO,EAAE,SAAS;IAI9B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAuGD,uDAAuD;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,SAA6B,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjG,gFAAgF;IAC1E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAOvD;;;;;OAKG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;IA2ChD;;;;;;OAMG;IACG,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;CAiBhC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtF"}
|
package/dist/s3.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { createHash, createHmac } from 'node:crypto';
|
|
2
|
+
const UNSIGNED = 'UNSIGNED-PAYLOAD';
|
|
3
|
+
const ALGORITHM = 'AWS4-HMAC-SHA256';
|
|
4
|
+
const SERVICE = 's3';
|
|
5
|
+
const sha256 = (data) => createHash('sha256').update(data).digest('hex');
|
|
6
|
+
const hmac = (key, data) => createHmac('sha256', key).update(data, 'utf8').digest();
|
|
7
|
+
/**
|
|
8
|
+
* Percent-encode one path segment the way SigV4 wants it.
|
|
9
|
+
*
|
|
10
|
+
* `encodeURIComponent` leaves `!'()*` alone and AWS does not, and it encodes
|
|
11
|
+
* nothing else differently — so this is that function plus five characters. A
|
|
12
|
+
* key containing any of them signs one way and is requested another, which is a
|
|
13
|
+
* 403 with no clue in it.
|
|
14
|
+
*/
|
|
15
|
+
function encodeSegment(segment) {
|
|
16
|
+
return encodeURIComponent(segment).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
17
|
+
}
|
|
18
|
+
/** Keys are `a/b/c`; each segment is encoded and the separators are not. */
|
|
19
|
+
const encodeKey = (key) => key.split('/').map(encodeSegment).join('/');
|
|
20
|
+
export class S3Error extends Error {
|
|
21
|
+
status;
|
|
22
|
+
method;
|
|
23
|
+
key;
|
|
24
|
+
constructor(status, method, key, body) {
|
|
25
|
+
// The body, because S3 and MinIO both answer with an XML document naming
|
|
26
|
+
// the exact code — SignatureDoesNotMatch, NoSuchBucket, AccessDenied — and
|
|
27
|
+
// a status alone sends the reader to the wrong one of six inputs.
|
|
28
|
+
super(`s3 ${method} ${key} failed: ${status} ${body.slice(0, 512)}`);
|
|
29
|
+
this.status = status;
|
|
30
|
+
this.method = method;
|
|
31
|
+
this.key = key;
|
|
32
|
+
this.name = 'S3Error';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class S3 {
|
|
36
|
+
#options;
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.#options = { ...options, endpoint: options.endpoint.replace(/\/+$/, '') };
|
|
39
|
+
}
|
|
40
|
+
get bucket() {
|
|
41
|
+
return this.#options.bucket;
|
|
42
|
+
}
|
|
43
|
+
#url(key) {
|
|
44
|
+
const { endpoint, bucket, forcePathStyle } = this.#options;
|
|
45
|
+
if (forcePathStyle)
|
|
46
|
+
return new URL(`${endpoint}/${bucket}/${encodeKey(key)}`);
|
|
47
|
+
const url = new URL(endpoint);
|
|
48
|
+
url.host = `${bucket}.${url.host}`;
|
|
49
|
+
url.pathname = `/${encodeKey(key)}`;
|
|
50
|
+
return url;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sign a request, returning the headers to send with it.
|
|
54
|
+
*
|
|
55
|
+
* The payload hash goes in `x-amz-content-sha256` and into the canonical
|
|
56
|
+
* request, so a body that changes in flight fails the signature rather than
|
|
57
|
+
* being stored. `UNSIGNED-PAYLOAD` is used for GET and DELETE, which have
|
|
58
|
+
* none — hashing the empty string would also work and this is what every
|
|
59
|
+
* other implementation sends.
|
|
60
|
+
*/
|
|
61
|
+
#sign(input) {
|
|
62
|
+
const { region, accessKey, secretKey } = this.#options;
|
|
63
|
+
const amzDate = input.now.toISOString().replace(/[-:]|\.\d{3}/g, '');
|
|
64
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
65
|
+
const headers = {
|
|
66
|
+
...input.headers,
|
|
67
|
+
host: input.url.host,
|
|
68
|
+
'x-amz-content-sha256': input.payloadHash,
|
|
69
|
+
'x-amz-date': amzDate,
|
|
70
|
+
};
|
|
71
|
+
// Lowercased, sorted, values trimmed — the canonical form. Getting the
|
|
72
|
+
// order wrong is a 403 that reads exactly like a wrong secret.
|
|
73
|
+
const canonicalHeaderNames = Object.keys(headers)
|
|
74
|
+
.map((h) => h.toLowerCase())
|
|
75
|
+
.sort();
|
|
76
|
+
const canonicalHeaders = canonicalHeaderNames
|
|
77
|
+
.map((name) => {
|
|
78
|
+
const key = Object.keys(headers).find((h) => h.toLowerCase() === name);
|
|
79
|
+
return `${name}:${String(headers[key]).trim()}\n`;
|
|
80
|
+
})
|
|
81
|
+
.join('');
|
|
82
|
+
const signedHeaders = canonicalHeaderNames.join(';');
|
|
83
|
+
const canonicalRequest = [
|
|
84
|
+
input.method,
|
|
85
|
+
input.url.pathname,
|
|
86
|
+
// No request here takes a query string. An empty one is still a line.
|
|
87
|
+
'',
|
|
88
|
+
canonicalHeaders,
|
|
89
|
+
signedHeaders,
|
|
90
|
+
input.payloadHash,
|
|
91
|
+
].join('\n');
|
|
92
|
+
const scope = `${dateStamp}/${region}/${SERVICE}/aws4_request`;
|
|
93
|
+
const stringToSign = [ALGORITHM, amzDate, scope, sha256(canonicalRequest)].join('\n');
|
|
94
|
+
const signingKey = hmac(hmac(hmac(hmac(`AWS4${secretKey}`, dateStamp), region), SERVICE), 'aws4_request');
|
|
95
|
+
const signature = createHmac('sha256', signingKey).update(stringToSign, 'utf8').digest('hex');
|
|
96
|
+
return {
|
|
97
|
+
...headers,
|
|
98
|
+
authorization: `${ALGORITHM} Credential=${accessKey}/${scope}, ` +
|
|
99
|
+
`SignedHeaders=${signedHeaders}, Signature=${signature}`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async #send(method, key, body, extraHeaders = {}) {
|
|
103
|
+
const url = this.#url(key);
|
|
104
|
+
const headers = this.#sign({
|
|
105
|
+
method,
|
|
106
|
+
url,
|
|
107
|
+
payloadHash: body === undefined ? UNSIGNED : sha256(body),
|
|
108
|
+
headers: body === undefined
|
|
109
|
+
? extraHeaders
|
|
110
|
+
: { ...extraHeaders, 'content-length': String(body.length) },
|
|
111
|
+
now: new Date(),
|
|
112
|
+
});
|
|
113
|
+
return fetch(url, {
|
|
114
|
+
method,
|
|
115
|
+
headers,
|
|
116
|
+
...(body === undefined ? {} : { body }),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Store an object, overwriting whatever was there. */
|
|
120
|
+
async put(key, body, contentType = 'application/octet-stream') {
|
|
121
|
+
const response = await this.#send('PUT', key, body, { 'content-type': contentType });
|
|
122
|
+
if (!response.ok)
|
|
123
|
+
throw new S3Error(response.status, 'PUT', key, await response.text());
|
|
124
|
+
}
|
|
125
|
+
/** Fetch an object. `undefined` when it is not there, which is not an error. */
|
|
126
|
+
async get(key) {
|
|
127
|
+
const response = await this.#send('GET', key);
|
|
128
|
+
if (response.status === 404)
|
|
129
|
+
return undefined;
|
|
130
|
+
if (!response.ok)
|
|
131
|
+
throw new S3Error(response.status, 'GET', key, await response.text());
|
|
132
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Remove an object. Absent is success.
|
|
136
|
+
*
|
|
137
|
+
* S3 answers `204` whether or not the key existed, and the caller — the
|
|
138
|
+
* collector, purging a deleted document — must be able to run twice.
|
|
139
|
+
*/
|
|
140
|
+
async remove(key) {
|
|
141
|
+
const response = await this.#send('DELETE', key);
|
|
142
|
+
if (!response.ok && response.status !== 404) {
|
|
143
|
+
throw new S3Error(response.status, 'DELETE', key, await response.text());
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A URL that fetches one object, for a while, without a credential.
|
|
148
|
+
*
|
|
149
|
+
* Query-string SigV4 rather than the header form: the point is a link a
|
|
150
|
+
* client can follow, so everything that authenticates it has to be in the
|
|
151
|
+
* URL. `host` is the only signed header, and the payload is unsigned because
|
|
152
|
+
* a GET has none.
|
|
153
|
+
*
|
|
154
|
+
* **This is a bearer capability and it leaves the permission model behind.**
|
|
155
|
+
* Nacre checks `read` once, when the link is minted, and the object store
|
|
156
|
+
* checks nothing afterwards — a revocation during the window does not reach a
|
|
157
|
+
* URL already handed out, and neither does a delete. That is what presigning
|
|
158
|
+
* is, in every implementation of it, and it is why the lifetime is a
|
|
159
|
+
* configured number rather than a constant: `NACRE_PRESIGN_TTL` is how long a
|
|
160
|
+
* deployment is willing for that to be true.
|
|
161
|
+
*
|
|
162
|
+
* It is also why the caller of this decides, not this: minting one per search
|
|
163
|
+
* hit would issue ten capabilities to answer a question about relevance, most
|
|
164
|
+
* never followed. See the note on the search response.
|
|
165
|
+
*/
|
|
166
|
+
presign(key, ttlSeconds) {
|
|
167
|
+
const { region, accessKey, secretKey } = this.#options;
|
|
168
|
+
const url = this.#url(key);
|
|
169
|
+
const now = new Date();
|
|
170
|
+
const amzDate = now.toISOString().replace(/[-:]|\.\d{3}/g, '');
|
|
171
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
172
|
+
const scope = `${dateStamp}/${region}/${SERVICE}/aws4_request`;
|
|
173
|
+
// Sorted by key, and encoded the same way the canonical request will be —
|
|
174
|
+
// a mismatch between the query that is signed and the query that is sent is
|
|
175
|
+
// a 403 with nothing in it about which of the two was wrong.
|
|
176
|
+
const params = [
|
|
177
|
+
['X-Amz-Algorithm', ALGORITHM],
|
|
178
|
+
['X-Amz-Credential', `${accessKey}/${scope}`],
|
|
179
|
+
['X-Amz-Date', amzDate],
|
|
180
|
+
['X-Amz-Expires', String(Math.max(1, Math.floor(ttlSeconds)))],
|
|
181
|
+
['X-Amz-SignedHeaders', 'host'],
|
|
182
|
+
];
|
|
183
|
+
const canonicalQuery = params
|
|
184
|
+
.map(([k, v]) => [encodeSegment(k), encodeSegment(v)])
|
|
185
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
186
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
187
|
+
.join('&');
|
|
188
|
+
const canonicalRequest = [
|
|
189
|
+
'GET',
|
|
190
|
+
url.pathname,
|
|
191
|
+
canonicalQuery,
|
|
192
|
+
`host:${url.host}\n`,
|
|
193
|
+
'host',
|
|
194
|
+
UNSIGNED,
|
|
195
|
+
].join('\n');
|
|
196
|
+
const stringToSign = [ALGORITHM, amzDate, scope, sha256(canonicalRequest)].join('\n');
|
|
197
|
+
const signingKey = hmac(hmac(hmac(hmac(`AWS4${secretKey}`, dateStamp), region), SERVICE), 'aws4_request');
|
|
198
|
+
const signature = createHmac('sha256', signingKey).update(stringToSign, 'utf8').digest('hex');
|
|
199
|
+
return `${url.origin}${url.pathname}?${canonicalQuery}&X-Amz-Signature=${signature}`;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Whether the bucket answers, for readiness.
|
|
203
|
+
*
|
|
204
|
+
* A HEAD on the bucket rather than on an object: it needs no key to exist and
|
|
205
|
+
* it fails for the three reasons that matter — unreachable, wrong bucket,
|
|
206
|
+
* wrong credential. A TCP connect would pass all three.
|
|
207
|
+
*/
|
|
208
|
+
async ready() {
|
|
209
|
+
const url = this.#options.forcePathStyle
|
|
210
|
+
? new URL(`${this.#options.endpoint}/${this.#options.bucket}`)
|
|
211
|
+
: this.#url('');
|
|
212
|
+
const headers = this.#sign({
|
|
213
|
+
method: 'HEAD',
|
|
214
|
+
url,
|
|
215
|
+
payloadHash: UNSIGNED,
|
|
216
|
+
headers: {},
|
|
217
|
+
now: new Date(),
|
|
218
|
+
});
|
|
219
|
+
const response = await fetch(url, { method: 'HEAD', headers });
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
throw new S3Error(response.status, 'HEAD', this.#options.bucket, response.statusText);
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Where a document's bytes live.
|
|
228
|
+
*
|
|
229
|
+
* Derived from identity — organization, layer, external id — and never from the
|
|
230
|
+
* content. Content addressing would let two documents share an object, and then
|
|
231
|
+
* deleting one would either strand the other or need a reference count that
|
|
232
|
+
* nothing else in this system has. One document, one object, and a re-ingest
|
|
233
|
+
* overwrites in place.
|
|
234
|
+
*
|
|
235
|
+
* The external id is hashed rather than escaped: it is caller-chosen text that
|
|
236
|
+
* may contain anything, and a key derived by escaping is a key whose length and
|
|
237
|
+
* shape a caller controls.
|
|
238
|
+
*/
|
|
239
|
+
export function documentKey(orgId, layerId, externalId) {
|
|
240
|
+
return `org/${orgId}/layer/${layerId}/${sha256(externalId)}`;
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=s3.js.map
|
package/dist/s3.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.js","sourceRoot":"","sources":["../s3.ts"],"names":[],"mappings":"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"}
|