@ratelkey/burrow-sdk 0.1.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/README.md +108 -0
- package/dist/cjs/Burrow.js +30 -0
- package/dist/cjs/Burrow.js.map +1 -0
- package/dist/cjs/BurrowClient.js +110 -0
- package/dist/cjs/BurrowClient.js.map +1 -0
- package/dist/cjs/Errors.js +80 -0
- package/dist/cjs/Errors.js.map +1 -0
- package/dist/cjs/Identity.js +274 -0
- package/dist/cjs/Identity.js.map +1 -0
- package/dist/cjs/Model.js +143 -0
- package/dist/cjs/Model.js.map +1 -0
- package/dist/cjs/Signer.js +42 -0
- package/dist/cjs/Signer.js.map +1 -0
- package/dist/cjs/Tls.js +85 -0
- package/dist/cjs/Tls.js.map +1 -0
- package/dist/cjs/Transport.js +198 -0
- package/dist/cjs/Transport.js.map +1 -0
- package/dist/cjs/index.js +28 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/Burrow.d.ts +59 -0
- package/dist/esm/Burrow.d.ts.map +1 -0
- package/dist/esm/Burrow.js +27 -0
- package/dist/esm/Burrow.js.map +1 -0
- package/dist/esm/BurrowClient.d.ts +40 -0
- package/dist/esm/BurrowClient.d.ts.map +1 -0
- package/dist/esm/BurrowClient.js +106 -0
- package/dist/esm/BurrowClient.js.map +1 -0
- package/dist/esm/Errors.d.ts +57 -0
- package/dist/esm/Errors.d.ts.map +1 -0
- package/dist/esm/Errors.js +67 -0
- package/dist/esm/Errors.js.map +1 -0
- package/dist/esm/Identity.d.ts +62 -0
- package/dist/esm/Identity.d.ts.map +1 -0
- package/dist/esm/Identity.js +269 -0
- package/dist/esm/Identity.js.map +1 -0
- package/dist/esm/Model.d.ts +75 -0
- package/dist/esm/Model.d.ts.map +1 -0
- package/dist/esm/Model.js +137 -0
- package/dist/esm/Model.js.map +1 -0
- package/dist/esm/Signer.d.ts +19 -0
- package/dist/esm/Signer.d.ts.map +1 -0
- package/dist/esm/Signer.js +39 -0
- package/dist/esm/Signer.js.map +1 -0
- package/dist/esm/Tls.d.ts +71 -0
- package/dist/esm/Tls.d.ts.map +1 -0
- package/dist/esm/Tls.js +79 -0
- package/dist/esm/Tls.js.map +1 -0
- package/dist/esm/Transport.d.ts +29 -0
- package/dist/esm/Transport.d.ts.map +1 -0
- package/dist/esm/Transport.js +195 -0
- package/dist/esm/Transport.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/package.json +1 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** One field of a structured secret: its name, current plaintext value, and declared type. */
|
|
2
|
+
export interface SecretField {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly value: string;
|
|
5
|
+
readonly type: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A secret read from a Burrow — fetched whole in a single signed call. Project it locally (no re-fetch):
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const db = await burrow.getSecret("payments/prod/DATABASE");
|
|
12
|
+
* db.value; // a single-value secret
|
|
13
|
+
* db.toMap(); // every field as name → value
|
|
14
|
+
* db.getField("password"); // one field
|
|
15
|
+
* const [user, pass] = db.getFields("username", "password"); // in order — destructures
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* A shape mismatch (e.g. `value` on a structured secret, or `toMap()` on a single one) throws
|
|
19
|
+
* {@link SecretShapeException}; a missing field throws {@link FieldNotFoundException}.
|
|
20
|
+
*/
|
|
21
|
+
export declare class Secret {
|
|
22
|
+
/** The secret's name (the final segment of its `project/env/NAME` coordinate). */
|
|
23
|
+
readonly name: string;
|
|
24
|
+
private readonly singleValue;
|
|
25
|
+
private readonly singleType;
|
|
26
|
+
private readonly singleVersion;
|
|
27
|
+
private readonly fieldsByName;
|
|
28
|
+
private constructor();
|
|
29
|
+
/** True when this is a structured (multi-field) secret rather than a single value. */
|
|
30
|
+
get isStructured(): boolean;
|
|
31
|
+
/** The single value. Throws {@link SecretShapeException} if this is a structured secret. */
|
|
32
|
+
get value(): string;
|
|
33
|
+
/** The declared type of a single-value secret; null for a structured one. */
|
|
34
|
+
get type(): string | null;
|
|
35
|
+
/** The current version of a single-value secret; null for a structured one. */
|
|
36
|
+
get version(): number | null;
|
|
37
|
+
/** Every field as a name → value map. Throws {@link SecretShapeException} if this is a single-value secret. */
|
|
38
|
+
toMap(): Record<string, string>;
|
|
39
|
+
/** The typed fields. Throws {@link SecretShapeException} if this is a single-value secret. */
|
|
40
|
+
fields(): SecretField[];
|
|
41
|
+
/**
|
|
42
|
+
* One field's value. Throws {@link SecretShapeException} if single-value, or
|
|
43
|
+
* {@link FieldNotFoundException} if absent.
|
|
44
|
+
*/
|
|
45
|
+
getField(field: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* The named fields' values, in the order given — for destructuring:
|
|
48
|
+
* `const [user, pass] = secret.getFields("username", "password")`. Throws {@link SecretShapeException} if
|
|
49
|
+
* single-value, or {@link FieldNotFoundException} if any name is absent.
|
|
50
|
+
*/
|
|
51
|
+
getFields(...names: string[]): string[];
|
|
52
|
+
private structured;
|
|
53
|
+
/** @internal */
|
|
54
|
+
static single(name: string, value: string, type: string, version: number): Secret;
|
|
55
|
+
/** @internal */
|
|
56
|
+
static structured(name: string, fields: ReadonlyMap<string, SecretField>): Secret;
|
|
57
|
+
}
|
|
58
|
+
/** A secret this machine can read, addressed by its `project / env / name` coordinate. */
|
|
59
|
+
export declare class SecretCoordinate {
|
|
60
|
+
readonly project: string;
|
|
61
|
+
readonly environment: string;
|
|
62
|
+
readonly name: string;
|
|
63
|
+
constructor(project: string, environment: string, name: string);
|
|
64
|
+
/** The `project/env/NAME` slug form, as the Burrow lists it and `BurrowClient.getSecret` takes it. */
|
|
65
|
+
toString(): string;
|
|
66
|
+
/** Parse a `project/env/NAME` slug into its parts. */
|
|
67
|
+
static parse(slug: string): SecretCoordinate;
|
|
68
|
+
}
|
|
69
|
+
/** A project this machine can read, and the environment categories it holds a grant for. */
|
|
70
|
+
export declare class ProjectAccess {
|
|
71
|
+
readonly project: string;
|
|
72
|
+
readonly categories: readonly string[];
|
|
73
|
+
constructor(project: string, categories: readonly string[]);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=Model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../../src/Model.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,MAAM;IAEf,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAN/B,OAAO;IASP,sFAAsF;IACtF,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,4FAA4F;IAC5F,IAAI,KAAK,IAAI,MAAM,CAOlB;IAED,6EAA6E;IAC7E,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI,CAExB;IAED,+EAA+E;IAC/E,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAE3B;IAED,+GAA+G;IAC/G,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAM/B,8FAA8F;IAC9F,MAAM,IAAI,WAAW,EAAE;IAIvB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAO/B;;;;OAIG;IACH,SAAS,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE;IASvC,OAAO,CAAC,UAAU;IAOlB,gBAAgB;IAChB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAIjF,gBAAgB;IAChB,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM;CAGlF;AAED,0FAA0F;AAC1F,qBAAa,gBAAgB;IAEzB,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAFZ,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM;IAGvB,sGAAsG;IACtG,QAAQ,IAAI,MAAM;IAIlB,sDAAsD;IACtD,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB;CAa7C;AAED,4FAA4F;AAC5F,qBAAa,aAAa;IAEtB,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE;gBAD7B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,SAAS,MAAM,EAAE;CAEzC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { FieldNotFoundException, SecretShapeException } from "./Errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* A secret read from a Burrow — fetched whole in a single signed call. Project it locally (no re-fetch):
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* const db = await burrow.getSecret("payments/prod/DATABASE");
|
|
7
|
+
* db.value; // a single-value secret
|
|
8
|
+
* db.toMap(); // every field as name → value
|
|
9
|
+
* db.getField("password"); // one field
|
|
10
|
+
* const [user, pass] = db.getFields("username", "password"); // in order — destructures
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* A shape mismatch (e.g. `value` on a structured secret, or `toMap()` on a single one) throws
|
|
14
|
+
* {@link SecretShapeException}; a missing field throws {@link FieldNotFoundException}.
|
|
15
|
+
*/
|
|
16
|
+
export class Secret {
|
|
17
|
+
name;
|
|
18
|
+
singleValue;
|
|
19
|
+
singleType;
|
|
20
|
+
singleVersion;
|
|
21
|
+
fieldsByName;
|
|
22
|
+
constructor(
|
|
23
|
+
/** The secret's name (the final segment of its `project/env/NAME` coordinate). */
|
|
24
|
+
name, singleValue, singleType, singleVersion, fieldsByName) {
|
|
25
|
+
this.name = name;
|
|
26
|
+
this.singleValue = singleValue;
|
|
27
|
+
this.singleType = singleType;
|
|
28
|
+
this.singleVersion = singleVersion;
|
|
29
|
+
this.fieldsByName = fieldsByName;
|
|
30
|
+
}
|
|
31
|
+
/** True when this is a structured (multi-field) secret rather than a single value. */
|
|
32
|
+
get isStructured() {
|
|
33
|
+
return this.fieldsByName !== null;
|
|
34
|
+
}
|
|
35
|
+
/** The single value. Throws {@link SecretShapeException} if this is a structured secret. */
|
|
36
|
+
get value() {
|
|
37
|
+
if (this.singleValue === null) {
|
|
38
|
+
throw new SecretShapeException(`'${this.name}' is a structured secret; use toMap(), getField(), or getFields()`);
|
|
39
|
+
}
|
|
40
|
+
return this.singleValue;
|
|
41
|
+
}
|
|
42
|
+
/** The declared type of a single-value secret; null for a structured one. */
|
|
43
|
+
get type() {
|
|
44
|
+
return this.singleType;
|
|
45
|
+
}
|
|
46
|
+
/** The current version of a single-value secret; null for a structured one. */
|
|
47
|
+
get version() {
|
|
48
|
+
return this.singleVersion;
|
|
49
|
+
}
|
|
50
|
+
/** Every field as a name → value map. Throws {@link SecretShapeException} if this is a single-value secret. */
|
|
51
|
+
toMap() {
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const [name, field] of this.structured())
|
|
54
|
+
out[name] = field.value;
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/** The typed fields. Throws {@link SecretShapeException} if this is a single-value secret. */
|
|
58
|
+
fields() {
|
|
59
|
+
return [...this.structured().values()];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* One field's value. Throws {@link SecretShapeException} if single-value, or
|
|
63
|
+
* {@link FieldNotFoundException} if absent.
|
|
64
|
+
*/
|
|
65
|
+
getField(field) {
|
|
66
|
+
const f = this.structured();
|
|
67
|
+
const hit = f.get(field);
|
|
68
|
+
if (hit === undefined)
|
|
69
|
+
throw new FieldNotFoundException(this.name, field, f.keys());
|
|
70
|
+
return hit.value;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The named fields' values, in the order given — for destructuring:
|
|
74
|
+
* `const [user, pass] = secret.getFields("username", "password")`. Throws {@link SecretShapeException} if
|
|
75
|
+
* single-value, or {@link FieldNotFoundException} if any name is absent.
|
|
76
|
+
*/
|
|
77
|
+
getFields(...names) {
|
|
78
|
+
const f = this.structured();
|
|
79
|
+
return names.map((n) => {
|
|
80
|
+
const hit = f.get(n);
|
|
81
|
+
if (hit === undefined)
|
|
82
|
+
throw new FieldNotFoundException(this.name, n, f.keys());
|
|
83
|
+
return hit.value;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
structured() {
|
|
87
|
+
if (this.fieldsByName === null) {
|
|
88
|
+
throw new SecretShapeException(`'${this.name}' is a single-value secret; use value`);
|
|
89
|
+
}
|
|
90
|
+
return this.fieldsByName;
|
|
91
|
+
}
|
|
92
|
+
/** @internal */
|
|
93
|
+
static single(name, value, type, version) {
|
|
94
|
+
return new Secret(name, value, type, version, null);
|
|
95
|
+
}
|
|
96
|
+
/** @internal */
|
|
97
|
+
static structured(name, fields) {
|
|
98
|
+
return new Secret(name, null, null, null, fields);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** A secret this machine can read, addressed by its `project / env / name` coordinate. */
|
|
102
|
+
export class SecretCoordinate {
|
|
103
|
+
project;
|
|
104
|
+
environment;
|
|
105
|
+
name;
|
|
106
|
+
constructor(project, environment, name) {
|
|
107
|
+
this.project = project;
|
|
108
|
+
this.environment = environment;
|
|
109
|
+
this.name = name;
|
|
110
|
+
}
|
|
111
|
+
/** The `project/env/NAME` slug form, as the Burrow lists it and `BurrowClient.getSecret` takes it. */
|
|
112
|
+
toString() {
|
|
113
|
+
return `${this.project}/${this.environment}/${this.name}`;
|
|
114
|
+
}
|
|
115
|
+
/** Parse a `project/env/NAME` slug into its parts. */
|
|
116
|
+
static parse(slug) {
|
|
117
|
+
// Split into at most three parts: a secret name may legitimately contain a slash, the coordinate's
|
|
118
|
+
// first two separators may not.
|
|
119
|
+
const first = slug.indexOf("/");
|
|
120
|
+
if (first < 0)
|
|
121
|
+
return new SecretCoordinate(slug, "", "");
|
|
122
|
+
const second = slug.indexOf("/", first + 1);
|
|
123
|
+
if (second < 0)
|
|
124
|
+
return new SecretCoordinate(slug.slice(0, first), slug.slice(first + 1), "");
|
|
125
|
+
return new SecretCoordinate(slug.slice(0, first), slug.slice(first + 1, second), slug.slice(second + 1));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** A project this machine can read, and the environment categories it holds a grant for. */
|
|
129
|
+
export class ProjectAccess {
|
|
130
|
+
project;
|
|
131
|
+
categories;
|
|
132
|
+
constructor(project, categories) {
|
|
133
|
+
this.project = project;
|
|
134
|
+
this.categories = categories;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=Model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.js","sourceRoot":"","sources":["../../src/Model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type KeyObject } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the Ed25519-signed `Ratel-*` headers for a machine-plane request. The canonical string MUST
|
|
4
|
+
* byte-match the Burrow's `MachineSignature.canonical`, or every read is rejected 401:
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* ACTION \n resource[0] \n … \n resource[n] \n TIMESTAMP \n NONCE \n CLIENT (no trailing newline)
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* TIMESTAMP is unix millis as a decimal string; the SAME string goes in the header and the signed bytes. A
|
|
11
|
+
* fresh timestamp + nonce per call makes a captured request unreplayable (the Burrow enforces a tight
|
|
12
|
+
* timestamp window and a single-use nonce).
|
|
13
|
+
*/
|
|
14
|
+
export declare const MachineSigner: {
|
|
15
|
+
canonical(action: string, resource: readonly string[], timestamp: string, nonce: string, client: string): Buffer;
|
|
16
|
+
/** The signed `Ratel-*` headers for one request. */
|
|
17
|
+
sign(machineId: string, key: KeyObject, action: string, resource: readonly string[], client: string, nowMillis: number): Record<string, string>;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=Signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Signer.d.ts","sourceRoot":"","sources":["../../src/Signer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAkC,MAAM,aAAa,CAAC;AAE7E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa;sBAEd,MAAM,YACJ,SAAS,MAAM,EAAE,aAChB,MAAM,SACV,MAAM,UACL,MAAM,GACb,MAAM;IAOT,oDAAoD;oBAEvC,MAAM,OACZ,SAAS,UACN,MAAM,YACJ,SAAS,MAAM,EAAE,UACnB,MAAM,aACH,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAmB1B,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { randomBytes, sign as signBytes } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the Ed25519-signed `Ratel-*` headers for a machine-plane request. The canonical string MUST
|
|
4
|
+
* byte-match the Burrow's `MachineSignature.canonical`, or every read is rejected 401:
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* ACTION \n resource[0] \n … \n resource[n] \n TIMESTAMP \n NONCE \n CLIENT (no trailing newline)
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* TIMESTAMP is unix millis as a decimal string; the SAME string goes in the header and the signed bytes. A
|
|
11
|
+
* fresh timestamp + nonce per call makes a captured request unreplayable (the Burrow enforces a tight
|
|
12
|
+
* timestamp window and a single-use nonce).
|
|
13
|
+
*/
|
|
14
|
+
export const MachineSigner = {
|
|
15
|
+
canonical(action, resource, timestamp, nonce, client) {
|
|
16
|
+
let out = `${action}\n`;
|
|
17
|
+
for (const part of resource)
|
|
18
|
+
out += `${part}\n`;
|
|
19
|
+
out += `${timestamp}\n${nonce}\n${client}`;
|
|
20
|
+
return Buffer.from(out, "utf8");
|
|
21
|
+
},
|
|
22
|
+
/** The signed `Ratel-*` headers for one request. */
|
|
23
|
+
sign(machineId, key, action, resource, client, nowMillis) {
|
|
24
|
+
const timestamp = String(nowMillis);
|
|
25
|
+
// Standard base64 with padding — the Burrow decodes with java.util.Base64's standard decoder. 16 bytes
|
|
26
|
+
// encodes to 24 characters, well inside the Burrow's 64-character nonce bound.
|
|
27
|
+
const nonce = randomBytes(16).toString("base64");
|
|
28
|
+
// Ed25519 signs the message directly: the digest algorithm must be null, not a hash name.
|
|
29
|
+
const signature = signBytes(null, MachineSigner.canonical(action, resource, timestamp, nonce, client), key).toString("base64");
|
|
30
|
+
return {
|
|
31
|
+
"Ratel-Machine": machineId,
|
|
32
|
+
"Ratel-Timestamp": timestamp,
|
|
33
|
+
"Ratel-Nonce": nonce,
|
|
34
|
+
"Ratel-Signature": signature,
|
|
35
|
+
"Ratel-Client": client,
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=Signer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Signer.js","sourceRoot":"","sources":["../../src/Signer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7E;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS,CACP,MAAc,EACd,QAA2B,EAC3B,SAAiB,EACjB,KAAa,EACb,MAAc;QAEd,IAAI,GAAG,GAAG,GAAG,MAAM,IAAI,CAAC;QACxB,KAAK,MAAM,IAAI,IAAI,QAAQ;YAAE,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC;QAChD,GAAG,IAAI,GAAG,SAAS,KAAK,KAAK,KAAK,MAAM,EAAE,CAAC;QAC3C,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,oDAAoD;IACpD,IAAI,CACF,SAAiB,EACjB,GAAc,EACd,MAAc,EACd,QAA2B,EAC3B,MAAc,EACd,SAAiB;QAEjB,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QACpC,uGAAuG;QACvG,+EAA+E;QAC/E,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACjD,0FAA0F;QAC1F,MAAM,SAAS,GAAG,SAAS,CACzB,IAAI,EACJ,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,EACnE,GAAG,CACJ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrB,OAAO;YACL,eAAe,EAAE,SAAS;YAC1B,iBAAiB,EAAE,SAAS;YAC5B,aAAa,EAAE,KAAK;YACpB,iBAAiB,EAAE,SAAS;YAC5B,cAAc,EAAE,MAAM;SACvB,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** How the SDK establishes trust in the Burrow's TLS certificate. */
|
|
2
|
+
export type TlsMode =
|
|
3
|
+
/**
|
|
4
|
+
* Trust-on-first-use. Burrows serve HTTPS self-signed by default, so there is no public CA to verify
|
|
5
|
+
* against — instead the Burrow's public key is pinned on first contact and a changed key is refused
|
|
6
|
+
* afterwards (like SSH host keys). The key rather than the certificate, so a Burrow that adds a domain or
|
|
7
|
+
* renews its certificate stays the same Burrow. The pin is kept beside that Burrow's identity, at
|
|
8
|
+
* `~/.burrow/burrows/<slug>/known_cert`, shared with the `ratel` CLI. This is the default and works with
|
|
9
|
+
* an out-of-box Burrow.
|
|
10
|
+
*/
|
|
11
|
+
{
|
|
12
|
+
readonly kind: "pinned";
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Pin to a specific key fingerprint: the SHA-256 of the Burrow's public key as it shows in its network
|
|
16
|
+
* settings (lowercase colon-separated hex; colons and case are ignored on comparison). No first-use
|
|
17
|
+
* trust — the key must match from the very first request.
|
|
18
|
+
*/
|
|
19
|
+
| {
|
|
20
|
+
readonly kind: "pinnedTo";
|
|
21
|
+
readonly fingerprint: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Standard trust: verify the certificate against the system CA store and the hostname. Use this when
|
|
25
|
+
* the Burrow serves a CA-issued certificate (a custom domain, or automatic Let's Encrypt).
|
|
26
|
+
*/
|
|
27
|
+
| {
|
|
28
|
+
readonly kind: "systemTrust";
|
|
29
|
+
};
|
|
30
|
+
/** The three trust modes, as values: `TlsMode.Pinned`, `TlsMode.PinnedTo("aa:bb:…")`, `TlsMode.SystemTrust`. */
|
|
31
|
+
export declare const TlsMode: {
|
|
32
|
+
readonly Pinned: TlsMode;
|
|
33
|
+
readonly PinnedTo: (fingerprint: string) => TlsMode;
|
|
34
|
+
readonly SystemTrust: TlsMode;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* SHA-256 of a certificate's PUBLIC KEY (its SubjectPublicKeyInfo) as lowercase colon-separated hex — the
|
|
38
|
+
* form the Burrow shows in Settings → Network and carries in a bootstrap code.
|
|
39
|
+
*
|
|
40
|
+
* The key rather than the whole certificate. A Burrow's certificate legitimately changes — a custom domain
|
|
41
|
+
* added to its SANs, an ACME renewal, a refresh — while the Burrow itself is the same one. Pinning the
|
|
42
|
+
* certificate turned each of those into "this is a different server", and a machine that had done nothing
|
|
43
|
+
* wrong stopped being able to read secrets.
|
|
44
|
+
*
|
|
45
|
+
* Parsed through `X509Certificate` and exported as SPKI DER rather than read off `getPeerCertificate().pubkey`,
|
|
46
|
+
* because that field's encoding is not documented to be SPKI and a pin has to hash exactly what the Burrow
|
|
47
|
+
* hashed.
|
|
48
|
+
*/
|
|
49
|
+
export declare function publicKeyPin(der: Uint8Array): string;
|
|
50
|
+
/** Compare two fingerprints ignoring case and separators, so "AA:BB…" and "aabb…" match. */
|
|
51
|
+
export declare function fingerprintsEqual(a: string, b: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* One Burrow's pinned key, at `<identity dir>/known_cert`, shared with the `ratel` CLI.
|
|
54
|
+
*
|
|
55
|
+
* Per burrow rather than one map for the machine. The pin and the identity are the same trust relationship:
|
|
56
|
+
* removing a Burrow should forget it entirely, and a shared file left the pin behind for a Burrow that no
|
|
57
|
+
* longer had an identity. It also means two Burrows can never contend for one file.
|
|
58
|
+
*
|
|
59
|
+
* A single value, not a map keyed by URL: the pin is the Burrow's KEY, so the same Burrow reached by its LAN
|
|
60
|
+
* address, its public address or its relay hostname presents the same one.
|
|
61
|
+
*
|
|
62
|
+
* Reads tolerate a missing or unreadable file (treated as unpinned); writes are atomic (temp file + rename)
|
|
63
|
+
* so concurrent SDK/CLI processes can't tear it. A null directory keeps the pin in memory only.
|
|
64
|
+
*/
|
|
65
|
+
export declare class PinStore {
|
|
66
|
+
private readonly file;
|
|
67
|
+
constructor(dir: string | null);
|
|
68
|
+
read(): string | null;
|
|
69
|
+
write(pin: string): void;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=Tls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tls.d.ts","sourceRoot":"","sources":["../../src/Tls.ts"],"names":[],"mappings":"AAIA,qEAAqE;AACrE,MAAM,MAAM,OAAO;AACjB;;;;;;;GAOG;AACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE;AAC7B;;;;GAIG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE;AAC7D;;;GAGG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC;AAErC,gHAAgH;AAChH,eAAO,MAAM,OAAO;qBACY,OAAO;qCACb,MAAM,KAAG,OAAO;0BACA,OAAO;CACvC,CAAC;AAEX;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAIpD;AAED,4FAA4F;AAC5F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAI/D;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;gBAEzB,GAAG,EAAE,MAAM,GAAG,IAAI;IAI9B,IAAI,IAAI,MAAM,GAAG,IAAI;IASrB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAazB"}
|
package/dist/esm/Tls.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createHash, X509Certificate } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
/** The three trust modes, as values: `TlsMode.Pinned`, `TlsMode.PinnedTo("aa:bb:…")`, `TlsMode.SystemTrust`. */
|
|
5
|
+
export const TlsMode = {
|
|
6
|
+
Pinned: { kind: "pinned" },
|
|
7
|
+
PinnedTo: (fingerprint) => ({ kind: "pinnedTo", fingerprint }),
|
|
8
|
+
SystemTrust: { kind: "systemTrust" },
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* SHA-256 of a certificate's PUBLIC KEY (its SubjectPublicKeyInfo) as lowercase colon-separated hex — the
|
|
12
|
+
* form the Burrow shows in Settings → Network and carries in a bootstrap code.
|
|
13
|
+
*
|
|
14
|
+
* The key rather than the whole certificate. A Burrow's certificate legitimately changes — a custom domain
|
|
15
|
+
* added to its SANs, an ACME renewal, a refresh — while the Burrow itself is the same one. Pinning the
|
|
16
|
+
* certificate turned each of those into "this is a different server", and a machine that had done nothing
|
|
17
|
+
* wrong stopped being able to read secrets.
|
|
18
|
+
*
|
|
19
|
+
* Parsed through `X509Certificate` and exported as SPKI DER rather than read off `getPeerCertificate().pubkey`,
|
|
20
|
+
* because that field's encoding is not documented to be SPKI and a pin has to hash exactly what the Burrow
|
|
21
|
+
* hashed.
|
|
22
|
+
*/
|
|
23
|
+
export function publicKeyPin(der) {
|
|
24
|
+
const spki = new X509Certificate(Buffer.from(der)).publicKey.export({ type: "spki", format: "der" });
|
|
25
|
+
const hex = createHash("sha256").update(spki).digest("hex");
|
|
26
|
+
return (hex.match(/../g) ?? []).join(":");
|
|
27
|
+
}
|
|
28
|
+
/** Compare two fingerprints ignoring case and separators, so "AA:BB…" and "aabb…" match. */
|
|
29
|
+
export function fingerprintsEqual(a, b) {
|
|
30
|
+
const norm = (s) => s.replace(/[^A-Za-z0-9]/g, "").toLowerCase();
|
|
31
|
+
const na = norm(a);
|
|
32
|
+
return na.length > 0 && na === norm(b);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One Burrow's pinned key, at `<identity dir>/known_cert`, shared with the `ratel` CLI.
|
|
36
|
+
*
|
|
37
|
+
* Per burrow rather than one map for the machine. The pin and the identity are the same trust relationship:
|
|
38
|
+
* removing a Burrow should forget it entirely, and a shared file left the pin behind for a Burrow that no
|
|
39
|
+
* longer had an identity. It also means two Burrows can never contend for one file.
|
|
40
|
+
*
|
|
41
|
+
* A single value, not a map keyed by URL: the pin is the Burrow's KEY, so the same Burrow reached by its LAN
|
|
42
|
+
* address, its public address or its relay hostname presents the same one.
|
|
43
|
+
*
|
|
44
|
+
* Reads tolerate a missing or unreadable file (treated as unpinned); writes are atomic (temp file + rename)
|
|
45
|
+
* so concurrent SDK/CLI processes can't tear it. A null directory keeps the pin in memory only.
|
|
46
|
+
*/
|
|
47
|
+
export class PinStore {
|
|
48
|
+
file;
|
|
49
|
+
constructor(dir) {
|
|
50
|
+
this.file = dir === null ? null : join(dir, "known_cert");
|
|
51
|
+
}
|
|
52
|
+
read() {
|
|
53
|
+
if (this.file === null)
|
|
54
|
+
return null;
|
|
55
|
+
try {
|
|
56
|
+
return readFileSync(this.file, "utf8").trim() || null;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
write(pin) {
|
|
63
|
+
if (this.file === null)
|
|
64
|
+
return;
|
|
65
|
+
if (this.read() === pin)
|
|
66
|
+
return;
|
|
67
|
+
// Best-effort: a read-only home just means the next process re-pins. Never fail a read over it.
|
|
68
|
+
try {
|
|
69
|
+
mkdirSync(dirname(this.file), { recursive: true });
|
|
70
|
+
const tmp = `${this.file}.tmp`;
|
|
71
|
+
writeFileSync(tmp, `${pin}\n`);
|
|
72
|
+
renameSync(tmp, this.file);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// ignored
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=Tls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tls.js","sourceRoot":"","sources":["../../src/Tls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAyB1C,gHAAgH;AAChH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAa;IACrC,QAAQ,EAAE,CAAC,WAAmB,EAAW,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;IAC/E,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,EAAa;CACvC,CAAC;AAEX;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAAC,GAAe;IAC1C,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACrG,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,iBAAiB,CAAC,CAAS,EAAE,CAAS;IACpD,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACjF,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,QAAQ;IACF,IAAI,CAAgB;IAErC,YAAY,GAAkB;QAC5B,IAAI,CAAC,IAAI,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACpC,IAAI,CAAC;YACH,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAW;QACf,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO;QAC/B,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG;YAAE,OAAO;QAChC,gGAAgG;QAChG,IAAI,CAAC;YACH,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,MAAM,CAAC;YAC/B,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC;YAC/B,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,UAAU;QACZ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type TlsMode } from "./Tls.js";
|
|
2
|
+
/**
|
|
3
|
+
* Performs signed machine-plane GETs against one Burrow, applying the configured {@link TlsMode}. Burrows
|
|
4
|
+
* serve HTTPS self-signed by default, so `TlsMode.Pinned` pins the certificate trust-on-first-use and
|
|
5
|
+
* refuses a changed one. Maps the Burrow's HTTP status + `{"error": …}` body to the typed
|
|
6
|
+
* {@link BurrowException}s, and surfaces the Burrow's own rejection reason verbatim.
|
|
7
|
+
*
|
|
8
|
+
* The TLS handshake is completed and the pin checked BEFORE the HTTP request is handed the socket, so a
|
|
9
|
+
* mismatched certificate never sees the signed `Ratel-*` headers. That ordering is the whole point of
|
|
10
|
+
* pinning, and it is why this dials the socket itself rather than letting the HTTP agent do it.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Transport {
|
|
13
|
+
private readonly tls;
|
|
14
|
+
private readonly base;
|
|
15
|
+
private readonly pins;
|
|
16
|
+
/** The fingerprint currently enforced. Null in trust-on-first-use mode until the first pin is learned. */
|
|
17
|
+
private pinned;
|
|
18
|
+
constructor(burrowUrl: string, tls: TlsMode,
|
|
19
|
+
/** This Burrow's identity directory — where its pinned key lives, beside the key that signs for it. */
|
|
20
|
+
identityDir: string | null);
|
|
21
|
+
/** Signed GET, returning the response body on 2xx; throws a typed {@link BurrowException} otherwise. */
|
|
22
|
+
get(path: string, headers: Record<string, string>): Promise<string>;
|
|
23
|
+
private resolve;
|
|
24
|
+
/** Dial the Burrow and, for the pinned modes, verify its certificate before anything is written. */
|
|
25
|
+
private connect;
|
|
26
|
+
private commitPinIfNew;
|
|
27
|
+
private errorFor;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=Transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transport.d.ts","sourceRoot":"","sources":["../../src/Transport.ts"],"names":[],"mappings":"AAaA,OAAO,EAA6C,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAUnF;;;;;;;;;GASG;AACH,qBAAa,SAAS;IAQlB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAPtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAW;IAChC,0GAA0G;IAC1G,OAAO,CAAC,MAAM,CAAgB;gBAG5B,SAAS,EAAE,MAAM,EACA,GAAG,EAAE,OAAO;IAC7B,uGAAuG;IACvG,WAAW,EAAE,MAAM,GAAG,IAAI;IAO5B,wGAAwG;IAClG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBzE,OAAO,CAAC,OAAO;IAgBf,oGAAoG;IACpG,OAAO,CAAC,OAAO;IAiFf,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,QAAQ;CAuBjB"}
|