salt-agent-sdk 0.1.0 → 0.19.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 +21 -0
- package/README.md +941 -30
- package/dist/actions.d.ts +21 -5
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +673 -54
- package/dist/actions.js.map +1 -1
- package/dist/ask.d.ts +83 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/ask.js +215 -0
- package/dist/ask.js.map +1 -0
- package/dist/chatPrivacy.d.ts +34 -0
- package/dist/chatPrivacy.d.ts.map +1 -0
- package/dist/chatPrivacy.js +60 -0
- package/dist/chatPrivacy.js.map +1 -0
- package/dist/client.d.ts +3123 -35
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1267 -143
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +16 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -5
- package/dist/config.js.map +1 -1
- package/dist/crypto.d.ts +62 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +121 -0
- package/dist/crypto.js.map +1 -1
- package/dist/delegations.d.ts +25 -8
- package/dist/delegations.d.ts.map +1 -1
- package/dist/delegations.js +67 -11
- package/dist/delegations.js.map +1 -1
- package/dist/device/client.d.ts +161 -0
- package/dist/device/client.d.ts.map +1 -0
- package/dist/device/client.js +601 -0
- package/dist/device/client.js.map +1 -0
- package/dist/device/host.d.ts +222 -0
- package/dist/device/host.d.ts.map +1 -0
- package/dist/device/host.js +891 -0
- package/dist/device/host.js.map +1 -0
- package/dist/device/http.d.ts +12 -0
- package/dist/device/http.d.ts.map +1 -0
- package/dist/device/http.js +124 -0
- package/dist/device/http.js.map +1 -0
- package/dist/device/index.d.ts +5 -0
- package/dist/device/index.d.ts.map +1 -0
- package/dist/device/index.js +27 -0
- package/dist/device/index.js.map +1 -0
- package/dist/device/protocol.d.ts +312 -0
- package/dist/device/protocol.d.ts.map +1 -0
- package/dist/device/protocol.js +435 -0
- package/dist/device/protocol.js.map +1 -0
- package/dist/identities.d.ts +9 -2
- package/dist/identities.d.ts.map +1 -1
- package/dist/identities.js +33 -3
- package/dist/identities.js.map +1 -1
- package/dist/identity.d.ts +155 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +196 -0
- package/dist/identity.js.map +1 -0
- package/dist/identityShare.d.ts +129 -0
- package/dist/identityShare.d.ts.map +1 -0
- package/dist/identityShare.js +308 -0
- package/dist/identityShare.js.map +1 -0
- package/dist/ids.d.ts +11 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +17 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/reconcile.d.ts +25 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +60 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/recovery.d.ts +51 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +59 -0
- package/dist/recovery.js.map +1 -0
- package/dist/register.d.ts +32 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +65 -0
- package/dist/register.js.map +1 -0
- package/dist/scriptJob.d.ts +92 -0
- package/dist/scriptJob.d.ts.map +1 -0
- package/dist/scriptJob.js +270 -0
- package/dist/scriptJob.js.map +1 -0
- package/dist/sessions.d.ts +131 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +240 -0
- package/dist/sessions.js.map +1 -0
- package/dist/socket.d.ts +115 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +1038 -0
- package/dist/socket.js.map +1 -0
- package/dist/webhook.d.ts +627 -28
- package/dist/webhook.d.ts.map +1 -1
- package/dist/webhook.js +1388 -84
- package/dist/webhook.js.map +1 -1
- package/dist/work.d.ts +57 -0
- package/dist/work.d.ts.map +1 -0
- package/dist/work.js +213 -0
- package/dist/work.js.map +1 -0
- package/package.json +26 -4
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Identity on Salt, R3 + R4 (design-fleet/runs/2026-09-22-identity/plan.md
|
|
3
|
+
// sections 5, 7, 8): sending a SIGNED SLICE of an agent's own identity
|
|
4
|
+
// sections into a chat as ordinary E2E ciphertext, answering an incoming
|
|
5
|
+
// ask for one, and revoking a disclosure already sent. Builds directly on
|
|
6
|
+
// identity.ts's R1 (the card vocabulary, canonicalizeJcs) and client.ts's
|
|
7
|
+
// R1 identity()/setIdentity() -- this module is what turns those into
|
|
8
|
+
// something an agent can actually SEND.
|
|
9
|
+
//
|
|
10
|
+
// Wire, inside the E2E plaintext, one marker line then (for SLICE) a JSON
|
|
11
|
+
// line:
|
|
12
|
+
//
|
|
13
|
+
// [[SALT-IDENTITY-SLICE id=01J9…]]
|
|
14
|
+
// {"sections":[{"key":"bio","value":"Forecasts for any city.","proof":null}],
|
|
15
|
+
// "signature":"-----BEGIN PGP SIGNATURE-----…"}
|
|
16
|
+
//
|
|
17
|
+
// [[SALT-IDENTITY-ASK id=01J9… keys=bio,link]]
|
|
18
|
+
// Mind sharing your bio and link?
|
|
19
|
+
//
|
|
20
|
+
// [[SALT-IDENTITY-DECLINE id=01J9… ask=01J8…]]
|
|
21
|
+
//
|
|
22
|
+
// [[SALT-IDENTITY-REVOKE id=01J9…]]
|
|
23
|
+
//
|
|
24
|
+
// `id` on SLICE/DECLINE/REVOKE is that message's OWN id (a fresh
|
|
25
|
+
// crypto.randomUUID()). `ask` (SLICE and DECLINE only) is the id of the
|
|
26
|
+
// SALT-IDENTITY-ASK this message answers -- present only when answering
|
|
27
|
+
// one, so an asker's client can resolve which question got answered. The
|
|
28
|
+
// signature on a SLICE is an OpenPGP ARMORED DETACHED signature
|
|
29
|
+
// (crypto.ts's signDetached), by the sender's own key, over
|
|
30
|
+
// `canonicalizeJcs(sections)` -- the sections array alone, not the whole
|
|
31
|
+
// payload object (identity.ts's canonicalizeJcs is the exact same RFC
|
|
32
|
+
// 8785 canonicalizer salt-api's card signing uses, reused here with an
|
|
33
|
+
// OpenPGP signature standing in for Salt's Ed25519 Web Bot Auth key -- a
|
|
34
|
+
// slice is signed as coming from the agent, never checked by Salt).
|
|
35
|
+
//
|
|
36
|
+
// One id names the WHOLE share, not one recipient's row: `share`'s ledger
|
|
37
|
+
// is per-RECIPIENT (salt-api keys a row on subject + recipient + id), but
|
|
38
|
+
// a single `id`, generated ONCE per share() call, is posted for every
|
|
39
|
+
// recipient's row -- so the one wire message's `id=`, the message_id
|
|
40
|
+
// PATCHed onto every row, and `revoke(id)` (which revokes every row under
|
|
41
|
+
// that id in one call) all refer to the same share, regardless of how
|
|
42
|
+
// many recipients it went to.
|
|
43
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
44
|
+
if (k2 === undefined) k2 = k;
|
|
45
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
46
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
47
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
48
|
+
}
|
|
49
|
+
Object.defineProperty(o, k2, desc);
|
|
50
|
+
}) : (function(o, m, k, k2) {
|
|
51
|
+
if (k2 === undefined) k2 = k;
|
|
52
|
+
o[k2] = m[k];
|
|
53
|
+
}));
|
|
54
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
55
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
56
|
+
}) : function(o, v) {
|
|
57
|
+
o["default"] = v;
|
|
58
|
+
});
|
|
59
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
60
|
+
var ownKeys = function(o) {
|
|
61
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
62
|
+
var ar = [];
|
|
63
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
64
|
+
return ar;
|
|
65
|
+
};
|
|
66
|
+
return ownKeys(o);
|
|
67
|
+
};
|
|
68
|
+
return function (mod) {
|
|
69
|
+
if (mod && mod.__esModule) return mod;
|
|
70
|
+
var result = {};
|
|
71
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
72
|
+
__setModuleDefault(result, mod);
|
|
73
|
+
return result;
|
|
74
|
+
};
|
|
75
|
+
})();
|
|
76
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
77
|
+
exports.IDENTITY_MARKER_PREFIX = void 0;
|
|
78
|
+
exports.parseIdentityMarker = parseIdentityMarker;
|
|
79
|
+
exports.formatIdentityAsk = formatIdentityAsk;
|
|
80
|
+
exports.formatIdentitySlice = formatIdentitySlice;
|
|
81
|
+
exports.formatIdentityDecline = formatIdentityDecline;
|
|
82
|
+
exports.formatIdentityRevoke = formatIdentityRevoke;
|
|
83
|
+
exports.createIdentitySharer = createIdentitySharer;
|
|
84
|
+
const node_crypto_1 = require("node:crypto");
|
|
85
|
+
const pgp = __importStar(require("./crypto"));
|
|
86
|
+
const identity_js_1 = require("./identity.js");
|
|
87
|
+
const ids_js_1 = require("./ids.js");
|
|
88
|
+
// --- Wire markers ----------------------------------------------------------
|
|
89
|
+
/** Every SALT-IDENTITY-* marker starts with this -- webhook.ts checks it before attempting the more specific parse, the same way it gates on delegations.FLOOR_REQUEST_MARKER. */
|
|
90
|
+
exports.IDENTITY_MARKER_PREFIX = "[[SALT-IDENTITY-";
|
|
91
|
+
const ID_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
92
|
+
const KEY_RE = /^[a-z0-9_]{1,64}$/;
|
|
93
|
+
const MARKER_RE = /^\[\[SALT-IDENTITY-(ASK|SLICE|DECLINE|REVOKE)([^\]\n]*)\]\]\n?/;
|
|
94
|
+
function parseAttrs(raw) {
|
|
95
|
+
const attrs = {};
|
|
96
|
+
for (const pair of raw.trim().split(/\s+/).filter(Boolean)) {
|
|
97
|
+
const eq = pair.indexOf("=");
|
|
98
|
+
if (eq > 0)
|
|
99
|
+
attrs[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
100
|
+
}
|
|
101
|
+
return attrs;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Parses one SALT-IDENTITY-* marker (plus, for SLICE, its JSON line) out of
|
|
105
|
+
* a decrypted plaintext. Returns null for anything that isn't one of these
|
|
106
|
+
* four markers, or that starts as one but is malformed (a bad id, an ASK
|
|
107
|
+
* with no keys, a SLICE whose JSON doesn't parse into {sections,
|
|
108
|
+
* signature}) -- webhook.ts treats either case as "starts with the prefix
|
|
109
|
+
* but nothing usable came out of it" and drops the message rather than
|
|
110
|
+
* guessing.
|
|
111
|
+
*/
|
|
112
|
+
function parseIdentityMarker(plaintext) {
|
|
113
|
+
const match = MARKER_RE.exec(plaintext || "");
|
|
114
|
+
if (!match)
|
|
115
|
+
return null;
|
|
116
|
+
const attrs = parseAttrs(match[2]);
|
|
117
|
+
const id = attrs.id || "";
|
|
118
|
+
if (!ID_RE.test(id))
|
|
119
|
+
return null;
|
|
120
|
+
const rest = plaintext.slice(match[0].length);
|
|
121
|
+
switch (match[1]) {
|
|
122
|
+
case "ASK": {
|
|
123
|
+
const keys = (attrs.keys || "")
|
|
124
|
+
.split(",")
|
|
125
|
+
.map((k) => k.trim())
|
|
126
|
+
.filter(Boolean);
|
|
127
|
+
if (keys.length === 0)
|
|
128
|
+
return null;
|
|
129
|
+
const text = rest.trim();
|
|
130
|
+
return { kind: "ask", id, keys, text: text || undefined };
|
|
131
|
+
}
|
|
132
|
+
case "SLICE": {
|
|
133
|
+
let parsed;
|
|
134
|
+
try {
|
|
135
|
+
parsed = JSON.parse(rest);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const payload = parsed;
|
|
141
|
+
if (!payload || !Array.isArray(payload.sections) || typeof payload.signature !== "string")
|
|
142
|
+
return null;
|
|
143
|
+
return { kind: "slice", id, askId: attrs.ask, payload: payload };
|
|
144
|
+
}
|
|
145
|
+
case "DECLINE":
|
|
146
|
+
return { kind: "decline", id, askId: attrs.ask };
|
|
147
|
+
case "REVOKE":
|
|
148
|
+
return { kind: "revoke", id };
|
|
149
|
+
default:
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** `[[SALT-IDENTITY-ASK id=… keys=k1,k2]]`, optionally followed by a text line. */
|
|
154
|
+
function formatIdentityAsk(id, keys, text) {
|
|
155
|
+
if (!ID_RE.test(id))
|
|
156
|
+
throw new Error("identity ask needs an id of letters, digits, _ or -.");
|
|
157
|
+
const clean = keys.map((k) => k.trim()).filter(Boolean);
|
|
158
|
+
if (clean.length === 0)
|
|
159
|
+
throw new Error("identity ask needs at least one section key.");
|
|
160
|
+
const line = `[[SALT-IDENTITY-ASK id=${id} keys=${clean.join(",")}]]`;
|
|
161
|
+
const body = (text || "").trim();
|
|
162
|
+
return body ? `${line}\n${body}` : line;
|
|
163
|
+
}
|
|
164
|
+
/** `[[SALT-IDENTITY-SLICE id=… [ask=…]]]` followed by the {sections, signature} JSON line. */
|
|
165
|
+
function formatIdentitySlice(id, sections, signature, askId) {
|
|
166
|
+
if (!ID_RE.test(id))
|
|
167
|
+
throw new Error("identity slice needs an id of letters, digits, _ or -.");
|
|
168
|
+
const attrs = askId ? `id=${id} ask=${askId}` : `id=${id}`;
|
|
169
|
+
return `[[SALT-IDENTITY-SLICE ${attrs}]]\n${JSON.stringify({ sections, signature })}`;
|
|
170
|
+
}
|
|
171
|
+
/** `[[SALT-IDENTITY-DECLINE id=… [ask=…]]]`. */
|
|
172
|
+
function formatIdentityDecline(id, askId) {
|
|
173
|
+
if (!ID_RE.test(id))
|
|
174
|
+
throw new Error("identity decline needs an id of letters, digits, _ or -.");
|
|
175
|
+
return askId ? `[[SALT-IDENTITY-DECLINE id=${id} ask=${askId}]]` : `[[SALT-IDENTITY-DECLINE id=${id}]]`;
|
|
176
|
+
}
|
|
177
|
+
/** `[[SALT-IDENTITY-REVOKE id=…]]`. */
|
|
178
|
+
function formatIdentityRevoke(id) {
|
|
179
|
+
if (!ID_RE.test(id))
|
|
180
|
+
throw new Error("identity revoke needs an id of letters, digits, _ or -.");
|
|
181
|
+
return `[[SALT-IDENTITY-REVOKE id=${id}]]`;
|
|
182
|
+
}
|
|
183
|
+
async function recipientMembers(client, caller, chatId) {
|
|
184
|
+
const members = await client.getChatMembers(caller.apiKey, chatId);
|
|
185
|
+
return members.filter((m) => !(0, ids_js_1.sameId)(m.id, caller.saltAppId) && !m.observer);
|
|
186
|
+
}
|
|
187
|
+
async function encryptToMembers(caller, members, plaintext) {
|
|
188
|
+
const recipientKeys = members.filter((m) => !!m.public_key).map((m) => m.public_key);
|
|
189
|
+
if (recipientKeys.length === 0)
|
|
190
|
+
throw new Error("No one in this chat has a usable public key -- nothing was sent.");
|
|
191
|
+
const message = await pgp.encryptFor(plaintext, recipientKeys);
|
|
192
|
+
const senderMessage = await pgp.encryptFor(plaintext, [caller.publicKey]);
|
|
193
|
+
return { message, senderMessage };
|
|
194
|
+
}
|
|
195
|
+
/** Builds the {share, ask, decline, disclosures, revoke} surface bound to one client. */
|
|
196
|
+
function createIdentitySharer(client, pgpPassphrase) {
|
|
197
|
+
async function share(caller, chatId, keys, opts = {}) {
|
|
198
|
+
const wanted = (keys || []).map((k) => k.trim()).filter(Boolean);
|
|
199
|
+
if (wanted.length === 0)
|
|
200
|
+
throw new Error("identity.share needs at least one section key.");
|
|
201
|
+
for (const key of wanted) {
|
|
202
|
+
if (!KEY_RE.test(key))
|
|
203
|
+
throw new Error(`identity.share: "${key}" is not a valid section key -- nothing was sent.`);
|
|
204
|
+
}
|
|
205
|
+
const recipients = await recipientMembers(client, caller, chatId);
|
|
206
|
+
if (recipients.length === 0)
|
|
207
|
+
throw new Error("No one to share with in this chat -- nothing was sent.");
|
|
208
|
+
// This agent's own sections. A key the agent has never stated is
|
|
209
|
+
// refused locally, before any ledger row is written. A section scoped
|
|
210
|
+
// to nobody is NOT refused: since 0.87.0 the share IS the grant -- the
|
|
211
|
+
// ledger row salt-api writes for this recipient is what admits them.
|
|
212
|
+
const mine = await client.identity(caller.apiKey);
|
|
213
|
+
const byKey = new Map(mine.sections.map((s) => [s.key, s]));
|
|
214
|
+
const sections = wanted.map((key) => {
|
|
215
|
+
const section = byKey.get(key);
|
|
216
|
+
if (!section || section.value == null) {
|
|
217
|
+
throw new Error(`identity.share: "${key}" is not a section this agent has -- nothing was sent.`);
|
|
218
|
+
}
|
|
219
|
+
const proof = section.kind === "proof" ? (section.checked_by ? { by: section.checked_by } : true) : null;
|
|
220
|
+
return { key, value: section.value, proof };
|
|
221
|
+
});
|
|
222
|
+
// One id for the WHOLE share -- posted as every recipient's ledger row
|
|
223
|
+
// (salt-api keys a row on subject + recipient + id, so the same id
|
|
224
|
+
// across recipients is exactly what creates one row each, not a
|
|
225
|
+
// collision), BEFORE any ciphertext, aborting on the first refusal
|
|
226
|
+
// (plan section 8's "0 bytes of section value reach the server" holds
|
|
227
|
+
// either way: this sends section KEYS, never a value). No claimed
|
|
228
|
+
// scope: a share may mix public and private sections, and salt-api
|
|
229
|
+
// records the narrowest real scope after the grant (0.86.1, 0.87.0).
|
|
230
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
231
|
+
for (const member of recipients) {
|
|
232
|
+
await client.postIdentityDisclosure(caller.apiKey, {
|
|
233
|
+
id,
|
|
234
|
+
section_keys: wanted,
|
|
235
|
+
chat_id: chatId,
|
|
236
|
+
recipient_id: member.id,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
const signature = await pgp.signDetached((0, identity_js_1.canonicalizeJcs)(sections), caller.privateKey, pgpPassphrase);
|
|
240
|
+
const plaintext = formatIdentitySlice(id, sections, signature, opts.ask);
|
|
241
|
+
const { message, senderMessage } = await encryptToMembers(caller, recipients, plaintext);
|
|
242
|
+
const posted = (await client.postMessage(caller.apiKey, chatId, message, senderMessage));
|
|
243
|
+
const messageId = (posted?.message_id ?? posted?.id);
|
|
244
|
+
try {
|
|
245
|
+
// One id, one PATCH -- it reaches every row under it.
|
|
246
|
+
await client.setIdentityDisclosureMessage(caller.apiKey, id, messageId);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// Best-effort: the slice is already sent and readable either way; a
|
|
250
|
+
// failed PATCH only means the ledger is missing the message_id it
|
|
251
|
+
// would otherwise carry.
|
|
252
|
+
}
|
|
253
|
+
return { id, messageId, recipients: recipients.map((m) => m.id) };
|
|
254
|
+
}
|
|
255
|
+
async function ask(caller, chatId, keys, text) {
|
|
256
|
+
const wanted = (keys || []).map((k) => k.trim()).filter(Boolean);
|
|
257
|
+
if (wanted.length === 0)
|
|
258
|
+
throw new Error("identity.ask needs at least one section key.");
|
|
259
|
+
const chat = await client.getChat(caller.apiKey, chatId);
|
|
260
|
+
if (!chat.users || chat.users.length !== 2) {
|
|
261
|
+
throw new Error("identity.ask can only be sent in a 1:1 chat -- nothing was sent.");
|
|
262
|
+
}
|
|
263
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
264
|
+
const plaintext = formatIdentityAsk(id, wanted, text);
|
|
265
|
+
const others = chat.users.filter((u) => !(0, ids_js_1.sameId)(u.id, caller.saltAppId));
|
|
266
|
+
const { message, senderMessage } = await encryptToMembers(caller, others, plaintext);
|
|
267
|
+
await client.postMessage(caller.apiKey, chatId, message, senderMessage);
|
|
268
|
+
return id;
|
|
269
|
+
}
|
|
270
|
+
async function decline(caller, chatId, askId) {
|
|
271
|
+
const recipients = await recipientMembers(client, caller, chatId);
|
|
272
|
+
if (recipients.length === 0)
|
|
273
|
+
throw new Error("No one to decline to in this chat -- nothing was sent.");
|
|
274
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
275
|
+
const plaintext = formatIdentityDecline(id, askId);
|
|
276
|
+
const { message, senderMessage } = await encryptToMembers(caller, recipients, plaintext);
|
|
277
|
+
await client.postMessage(caller.apiKey, chatId, message, senderMessage);
|
|
278
|
+
return { id };
|
|
279
|
+
}
|
|
280
|
+
async function disclosures(caller, opts) {
|
|
281
|
+
const result = await client.listIdentityDisclosures(caller.apiKey, opts);
|
|
282
|
+
return result.disclosures ?? [];
|
|
283
|
+
}
|
|
284
|
+
async function revoke(caller, id) {
|
|
285
|
+
// Revokes every ledger row under `id` (every recipient) in one call; the
|
|
286
|
+
// API answers `{disclosures: [...]}`, one row per recipient, all in the
|
|
287
|
+
// same chat, so one REVOKE marker into that chat covers the share.
|
|
288
|
+
const { disclosures: rows } = await client.revokeIdentityDisclosure(caller.apiKey, id);
|
|
289
|
+
const chatId = rows[0]?.chat_id;
|
|
290
|
+
const plaintext = formatIdentityRevoke(id);
|
|
291
|
+
try {
|
|
292
|
+
if (chatId) {
|
|
293
|
+
const recipients = await recipientMembers(client, caller, chatId);
|
|
294
|
+
if (recipients.length > 0) {
|
|
295
|
+
const { message, senderMessage } = await encryptToMembers(caller, recipients, plaintext);
|
|
296
|
+
await client.postMessage(caller.apiKey, chatId, message, senderMessage);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Best-effort: the rows are revoked server-side (nothing new will be
|
|
302
|
+
// served for them) even if telling this chat about it right now failed.
|
|
303
|
+
}
|
|
304
|
+
return rows;
|
|
305
|
+
}
|
|
306
|
+
return { share, ask, decline, disclosures, revoke };
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=identityShare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identityShare.js","sourceRoot":"","sources":["../src/identityShare.ts"],"names":[],"mappings":"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"}
|
package/dist/ids.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type SaltId = string;
|
|
2
|
+
/**
|
|
3
|
+
* Do two ids name the same record?
|
|
4
|
+
*
|
|
5
|
+
* Ids reach this SDK from several places -- a webhook body, a REST response,
|
|
6
|
+
* an env var, a model's tool call -- and only some of them are guaranteed to
|
|
7
|
+
* be canonically cased. Comparing through here rather than with `===` keeps a
|
|
8
|
+
* capitalisation difference from reading as a different record.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sameId(a: unknown, b: unknown): boolean;
|
|
11
|
+
//# sourceMappingURL=ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAGtD"}
|
package/dist/ids.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sameId = sameId;
|
|
4
|
+
/**
|
|
5
|
+
* Do two ids name the same record?
|
|
6
|
+
*
|
|
7
|
+
* Ids reach this SDK from several places -- a webhook body, a REST response,
|
|
8
|
+
* an env var, a model's tool call -- and only some of them are guaranteed to
|
|
9
|
+
* be canonically cased. Comparing through here rather than with `===` keeps a
|
|
10
|
+
* capitalisation difference from reading as a different record.
|
|
11
|
+
*/
|
|
12
|
+
function sameId(a, b) {
|
|
13
|
+
if (a == null || b == null)
|
|
14
|
+
return false;
|
|
15
|
+
return String(a).toLowerCase() === String(b).toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=ids.js.map
|
package/dist/ids.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":";;AAsBA,wBAGC;AAXD;;;;;;;GAOG;AACH,SAAgB,MAAM,CAAC,CAAU,EAAE,CAAU;IAC3C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IACzC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AAC7D,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
export * from "./ids";
|
|
1
2
|
export * from "./client";
|
|
2
3
|
export * from "./crypto";
|
|
4
|
+
export * from "./recovery";
|
|
5
|
+
export * from "./identity";
|
|
6
|
+
export * from "./identityShare";
|
|
3
7
|
export * from "./identities";
|
|
8
|
+
export * from "./register";
|
|
9
|
+
export * from "./reconcile";
|
|
4
10
|
export * from "./delegations";
|
|
11
|
+
export * from "./work";
|
|
12
|
+
export * from "./sessions";
|
|
13
|
+
export * from "./scriptJob";
|
|
14
|
+
export * from "./chatPrivacy";
|
|
5
15
|
export * from "./webhook";
|
|
16
|
+
export * from "./ask";
|
|
17
|
+
export * from "./socket";
|
|
6
18
|
export * from "./actions";
|
|
7
19
|
export * from "./config";
|
|
20
|
+
export * from "./device/index.js";
|
|
8
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,11 +14,24 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./ids"), exports);
|
|
17
18
|
__exportStar(require("./client"), exports);
|
|
18
19
|
__exportStar(require("./crypto"), exports);
|
|
20
|
+
__exportStar(require("./recovery"), exports);
|
|
21
|
+
__exportStar(require("./identity"), exports);
|
|
22
|
+
__exportStar(require("./identityShare"), exports);
|
|
19
23
|
__exportStar(require("./identities"), exports);
|
|
24
|
+
__exportStar(require("./register"), exports);
|
|
25
|
+
__exportStar(require("./reconcile"), exports);
|
|
20
26
|
__exportStar(require("./delegations"), exports);
|
|
27
|
+
__exportStar(require("./work"), exports);
|
|
28
|
+
__exportStar(require("./sessions"), exports);
|
|
29
|
+
__exportStar(require("./scriptJob"), exports);
|
|
30
|
+
__exportStar(require("./chatPrivacy"), exports);
|
|
21
31
|
__exportStar(require("./webhook"), exports);
|
|
32
|
+
__exportStar(require("./ask"), exports);
|
|
33
|
+
__exportStar(require("./socket"), exports);
|
|
22
34
|
__exportStar(require("./actions"), exports);
|
|
23
35
|
__exportStar(require("./config"), exports);
|
|
36
|
+
__exportStar(require("./device/index.js"), exports);
|
|
24
37
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,2CAAyB;AACzB,+CAA6B;AAC7B,gDAA8B;AAC9B,4CAA0B;AAC1B,4CAA0B;AAC1B,2CAAyB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wCAAsB;AACtB,2CAAyB;AACzB,2CAAyB;AACzB,6CAA2B;AAC3B,6CAA2B;AAC3B,kDAAgC;AAChC,+CAA6B;AAC7B,6CAA2B;AAC3B,8CAA4B;AAC5B,gDAA8B;AAC9B,yCAAuB;AACvB,6CAA2B;AAC3B,8CAA4B;AAC5B,gDAA8B;AAC9B,4CAA0B;AAC1B,wCAAsB;AACtB,2CAAyB;AACzB,4CAA0B;AAC1B,2CAAyB;AACzB,oDAAkC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SaltClient } from "./client";
|
|
2
|
+
import type { IdentityStore } from "./identities";
|
|
3
|
+
import { type SaltId } from "./ids.js";
|
|
4
|
+
export interface ReconcileLogger {
|
|
5
|
+
info: (msg: string) => void;
|
|
6
|
+
error: (msg: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export interface ReconcileResult {
|
|
9
|
+
/** Identities whose stored id already matched what salt-api reports. */
|
|
10
|
+
unchanged: number;
|
|
11
|
+
/** Identities moved to the id salt-api reports for their api key. */
|
|
12
|
+
reassigned: Array<{
|
|
13
|
+
username: string;
|
|
14
|
+
from: SaltId;
|
|
15
|
+
to: SaltId;
|
|
16
|
+
}>;
|
|
17
|
+
/** Identities left exactly as they were because the API could not be asked. */
|
|
18
|
+
failed: Array<{
|
|
19
|
+
username: string;
|
|
20
|
+
saltAppId: SaltId;
|
|
21
|
+
error: string;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
export declare function reconcileIdentityIds(store: IdentityStore, client: Pick<SaltClient, "whoAmI">, logger?: ReconcileLogger): Promise<ReconcileResult>;
|
|
25
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,UAAU,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,+EAA+E;IAC/E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAClC,MAAM,GAAE,eAA6D,GACpE,OAAO,CAAC,eAAe,CAAC,CA0C1B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Re-key hosted identities to the ids salt-api actually uses for them.
|
|
3
|
+
//
|
|
4
|
+
// Every webhook is routed by Salt id: salt-api puts `agent.id` in
|
|
5
|
+
// X-Salt-Agent-Id and the server finds the signing key with
|
|
6
|
+
// `identities.get(thatId)`. The store, though, holds whatever id was current
|
|
7
|
+
// when the identity was REGISTERED -- an env var, a create response -- and
|
|
8
|
+
// nothing ever revisited it. When salt-api moved every primary key to a uuid
|
|
9
|
+
// (Sep 2026) each hosted identity kept its integer id on disk, every lookup
|
|
10
|
+
// missed, and every message to those agents was rejected as "no signing key"
|
|
11
|
+
// while health checks stayed green: the dot reports liveness, not delivery.
|
|
12
|
+
//
|
|
13
|
+
// So the stored id is a hint and the API is the truth. An api key belongs to
|
|
14
|
+
// exactly one agent, and GET /agents/webhook_secret answers with that agent's
|
|
15
|
+
// canonical id, so confirming an identity is one call. Run this at boot before
|
|
16
|
+
// serving; createWebhookServer also runs it when a lookup misses, so an id
|
|
17
|
+
// change while the process is up heals on the first affected delivery instead
|
|
18
|
+
// of waiting for a restart.
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.reconcileIdentityIds = reconcileIdentityIds;
|
|
21
|
+
const ids_js_1 = require("./ids.js");
|
|
22
|
+
async function reconcileIdentityIds(store, client, logger = { info: console.log, error: console.error }) {
|
|
23
|
+
const result = { unchanged: 0, reassigned: [], failed: [] };
|
|
24
|
+
// all() is a snapshot, so re-keying inside the loop is safe.
|
|
25
|
+
for (const identity of store.all()) {
|
|
26
|
+
const label = identity.username || String(identity.saltAppId);
|
|
27
|
+
let canonical;
|
|
28
|
+
let failure;
|
|
29
|
+
try {
|
|
30
|
+
canonical = (await client.whoAmI(identity.apiKey)).agent_id;
|
|
31
|
+
if (!canonical)
|
|
32
|
+
failure = "no agent_id in the response";
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
failure = err.message;
|
|
36
|
+
}
|
|
37
|
+
if (failure !== undefined || !canonical) {
|
|
38
|
+
// A revoked key or an unreachable API is not a reason to touch the
|
|
39
|
+
// store. The identity stays exactly as it is; deleting or guessing here
|
|
40
|
+
// would turn a transient outage into lost keys.
|
|
41
|
+
const error = failure ?? "no agent_id in the response";
|
|
42
|
+
result.failed.push({ username: label, saltAppId: identity.saltAppId, error });
|
|
43
|
+
logger.error(`[identities] could not confirm the id of ${label} (${identity.saltAppId}): ${error}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if ((0, ids_js_1.sameId)(canonical, identity.saltAppId)) {
|
|
47
|
+
result.unchanged += 1;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const from = identity.saltAppId;
|
|
51
|
+
const alreadyRegistered = store.get(canonical) !== undefined;
|
|
52
|
+
store.reassignId(from, canonical);
|
|
53
|
+
result.reassigned.push({ username: label, from, to: canonical });
|
|
54
|
+
logger.info(alreadyRegistered
|
|
55
|
+
? `[identities] ${label}: dropped the stale entry under ${from}; already registered as ${canonical}`
|
|
56
|
+
: `[identities] ${label}: re-keyed ${from} -> ${canonical}`);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":";AAAA,uEAAuE;AACvE,EAAE;AACF,kEAAkE;AAClE,4DAA4D;AAC5D,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,4BAA4B;;AAoB5B,oDA8CC;AA9DD,qCAA+C;AAgBxC,KAAK,UAAU,oBAAoB,CACxC,KAAoB,EACpB,MAAkC,EAClC,SAA0B,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE;IAErE,MAAM,MAAM,GAAoB,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAE7E,6DAA6D;IAC7D,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAE9D,IAAI,SAA6B,CAAC;QAClC,IAAI,OAA2B,CAAC;QAChC,IAAI,CAAC;YACH,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC5D,IAAI,CAAC,SAAS;gBAAE,OAAO,GAAG,6BAA6B,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAI,GAAa,CAAC,OAAO,CAAC;QACnC,CAAC;QACD,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;YACxC,mEAAmE;YACnE,wEAAwE;YACxE,gDAAgD;YAChD,MAAM,KAAK,GAAG,OAAO,IAAI,6BAA6B,CAAC;YACvD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9E,MAAM,CAAC,KAAK,CAAC,4CAA4C,KAAK,KAAK,QAAQ,CAAC,SAAS,MAAM,KAAK,EAAE,CAAC,CAAC;YACpG,SAAS;QACX,CAAC;QAED,IAAI,IAAA,eAAM,EAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1C,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;YACtB,SAAS;QACX,CAAC;QAED,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC;QAChC,MAAM,iBAAiB,GAAG,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC;QAC7D,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAClC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CACT,iBAAiB;YACf,CAAC,CAAC,gBAAgB,KAAK,mCAAmC,IAAI,2BAA2B,SAAS,EAAE;YACpG,CAAC,CAAC,gBAAgB,KAAK,cAAc,IAAI,OAAO,SAAS,EAAE,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SaltId } from "./ids.js";
|
|
2
|
+
/** One row of GET /api/v1/recovery_request/incoming, and the body of a `recovery_share_requested` delivery (which adds `type`). */
|
|
3
|
+
export interface RecoveryRequestForGuardian {
|
|
4
|
+
id: SaltId;
|
|
5
|
+
requester_id: SaltId;
|
|
6
|
+
requester_username?: string;
|
|
7
|
+
requester_display_name?: string;
|
|
8
|
+
threshold: number | null;
|
|
9
|
+
collected: number;
|
|
10
|
+
already_contributed: boolean;
|
|
11
|
+
/** Armored PGP public key minted by the requester for this attempt alone. */
|
|
12
|
+
ephemeral_public_key: string;
|
|
13
|
+
created_at: string;
|
|
14
|
+
expires_at: string;
|
|
15
|
+
type?: "recovery_share_requested";
|
|
16
|
+
}
|
|
17
|
+
/** A guardian seat awaiting (or carrying) this account's answer: GET /api/v1/recovery_shares/invitations, and the body of a `recovery_guardian_invited` delivery. */
|
|
18
|
+
export interface RecoveryGuardianInvitation {
|
|
19
|
+
id: SaltId;
|
|
20
|
+
status: "pending" | "accepted" | "declined";
|
|
21
|
+
owner_id: SaltId;
|
|
22
|
+
owner_username?: string;
|
|
23
|
+
owner_display_name?: string;
|
|
24
|
+
threshold: number;
|
|
25
|
+
created_at: string;
|
|
26
|
+
type?: "recovery_guardian_invited";
|
|
27
|
+
}
|
|
28
|
+
/** One row of GET /api/v1/recovery_shares/held: a share this guardian holds for `owner_id`. */
|
|
29
|
+
export interface HeldRecoveryShare {
|
|
30
|
+
owner_id: SaltId;
|
|
31
|
+
owner_username?: string;
|
|
32
|
+
owner_display_name?: string;
|
|
33
|
+
/** Armored PGP message, encrypted to this guardian's public key. */
|
|
34
|
+
encrypted_share: string;
|
|
35
|
+
threshold: number;
|
|
36
|
+
}
|
|
37
|
+
/** Re-seal a plaintext share to the requester's ephemeral public key (salt-fe `sealShareForRequester`). */
|
|
38
|
+
export declare function sealShareForRequester(plainShareB64: string, ephemeralPublicKey: string): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Open the share held for `ownerId` with the guardian's own key and re-seal
|
|
41
|
+
* it to the request's ephemeral key. Returns the armor to POST as
|
|
42
|
+
* `sealed_share`. Throws if no share is held for that owner, or the share
|
|
43
|
+
* cannot be opened with this key.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resealHeldShare(params: {
|
|
46
|
+
held: HeldRecoveryShare[];
|
|
47
|
+
request: Pick<RecoveryRequestForGuardian, "requester_id" | "ephemeral_public_key">;
|
|
48
|
+
privateKey: string;
|
|
49
|
+
passphrase: string;
|
|
50
|
+
}): Promise<string>;
|
|
51
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,mIAAmI;AACnI,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,6EAA6E;IAC7E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,0BAA0B,CAAC;CACnC;AAED,qKAAqK;AACrK,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,2BAA2B,CAAC;CACpC;AAED,+FAA+F;AAC/F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,2GAA2G;AAC3G,wBAAsB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE9G;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,OAAO,EAAE,IAAI,CAAC,0BAA0B,EAAE,cAAc,GAAG,sBAAsB,CAAC,CAAC;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlB"}
|
package/dist/recovery.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sealShareForRequester = sealShareForRequester;
|
|
4
|
+
exports.resealHeldShare = resealHeldShare;
|
|
5
|
+
// Social recovery, guardian side (salt-api parity both ways, 2026-10-05: an
|
|
6
|
+
// agent may be a recovery guardian). Mirrors salt-fe's GuardianRequests.jsx
|
|
7
|
+
// and utilities/socialRecovery.js `sealShareForRequester`:
|
|
8
|
+
//
|
|
9
|
+
// 1. The owner sealed a Shamir share to THIS guardian's PGP public key
|
|
10
|
+
// (GET /api/v1/recovery_shares/held returns the armor).
|
|
11
|
+
// 2. Someone starts a recovery. The guardian learns of it as a
|
|
12
|
+
// `recovery_share_requested` delivery (webhook or socket outbox) or by
|
|
13
|
+
// GET /api/v1/recovery_request/incoming; each request carries the
|
|
14
|
+
// requester's throwaway `ephemeral_public_key`.
|
|
15
|
+
// 3. The guardian opens its share with its OWN key and re-seals the same
|
|
16
|
+
// plaintext to that ephemeral key, then
|
|
17
|
+
// POST /api/v1/recovery_requests/:id/contribute { sealed_share }.
|
|
18
|
+
//
|
|
19
|
+
// A seat counts only once the guardian ACCEPTED it
|
|
20
|
+
// (POST /api/v1/recovery_shares/:id/accept); until then Salt holds the share
|
|
21
|
+
// back and the guardian can release nothing.
|
|
22
|
+
//
|
|
23
|
+
// Salt never sees the share. The plaintext is a base64 Shamir share and is
|
|
24
|
+
// re-sealed byte for byte -- do not parse or alter it. Combining shares is the
|
|
25
|
+
// REQUESTER's browser's job; nothing here needs a Shamir implementation.
|
|
26
|
+
//
|
|
27
|
+
// `client.releaseRecoveryShare` takes ONLY a request id and reads the
|
|
28
|
+
// requester and the ephemeral key from Salt's own `incoming` record. Never
|
|
29
|
+
// accept a requester id or an ephemeral key from a delivery body, a prompt
|
|
30
|
+
// or a tool argument: pairing a victim's share with an attacker's key is the
|
|
31
|
+
// whole attack (confused deputy). `resealHeldShare` is exported for hosts
|
|
32
|
+
// that must do the steps themselves; feed it the server's record, nothing else.
|
|
33
|
+
//
|
|
34
|
+
// Checking the request is genuine is the guardian's judgement, exactly as it
|
|
35
|
+
// is for a person ("check with them another way first"): releasing a share to
|
|
36
|
+
// a bogus request is how a stolen session becomes an account recovery. An
|
|
37
|
+
// agent host should gate `releaseRecoveryShare` behind whatever approval its
|
|
38
|
+
// owner configured; this helper does not decide.
|
|
39
|
+
const crypto_js_1 = require("./crypto.js");
|
|
40
|
+
/** Re-seal a plaintext share to the requester's ephemeral public key (salt-fe `sealShareForRequester`). */
|
|
41
|
+
async function sealShareForRequester(plainShareB64, ephemeralPublicKey) {
|
|
42
|
+
return (0, crypto_js_1.encryptFor)(plainShareB64, [ephemeralPublicKey]);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Open the share held for `ownerId` with the guardian's own key and re-seal
|
|
46
|
+
* it to the request's ephemeral key. Returns the armor to POST as
|
|
47
|
+
* `sealed_share`. Throws if no share is held for that owner, or the share
|
|
48
|
+
* cannot be opened with this key.
|
|
49
|
+
*/
|
|
50
|
+
async function resealHeldShare(params) {
|
|
51
|
+
const mine = params.held.find((h) => String(h.owner_id) === String(params.request.requester_id));
|
|
52
|
+
if (!mine)
|
|
53
|
+
throw new Error("no share held for this requester");
|
|
54
|
+
const plain = await (0, crypto_js_1.decrypt)(mine.encrypted_share, params.privateKey, params.passphrase);
|
|
55
|
+
if (!plain)
|
|
56
|
+
throw new Error("could not open the held share with this key");
|
|
57
|
+
return sealShareForRequester(plain, params.request.ephemeral_public_key);
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=recovery.js.map
|