@tinycloud/courier 0.6.0-beta.18 → 0.6.0-beta.20

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.
@@ -0,0 +1,271 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { ulid } from 'ulid';
4
+ import { activateValidatedRuntimeDelegation } from '@tinycloud/node-sdk';
5
+ import { assertApprovalRequestBinding, assertInvitationApprovalGrant, parseApprovalSiwe } from './authority.js';
6
+ import { CourierError } from './errors.js';
7
+ import { mkdirOwnerOnly, profileDir, writeSecure } from './profile.js';
8
+ const DAY_MS = 86_400_000;
9
+ const pendingDir = (home = profileDir()) => path.join(home, 'pending-invites');
10
+ const grantDir = (home = profileDir()) => path.join(home, 'invite-grants');
11
+ const ULID = /^[0-9A-HJKMNP-TV-Z]{26}$/;
12
+ function readJson(file) {
13
+ try {
14
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
15
+ }
16
+ catch (error) {
17
+ if (error.code === 'ENOENT')
18
+ return undefined;
19
+ throw new CourierError('bad-profile', `Invalid ${path.basename(file)}: ${error.message}`);
20
+ }
21
+ }
22
+ function secureWrite(dir, id, value) {
23
+ mkdirOwnerOnly(dir);
24
+ writeSecure(path.join(dir, `${id}.json`), value);
25
+ }
26
+ export function loadPendingInvite(invitationId, home = profileDir()) {
27
+ if (!ULID.test(invitationId))
28
+ return undefined;
29
+ return readJson(path.join(pendingDir(home), `${invitationId}.json`));
30
+ }
31
+ export function savePendingInvite(pending) {
32
+ secureWrite(pendingDir(), pending.invitationId, pending);
33
+ }
34
+ export function loadInviteGrant(invitationId, home = profileDir()) {
35
+ if (!ULID.test(invitationId))
36
+ return undefined;
37
+ return readJson(path.join(grantDir(home), `${invitationId}.json`));
38
+ }
39
+ /** The open (not closed, not sent, not yet expired) pending approval for this exact session and recipient, if any. */
40
+ export function findPendingInvite(profile, sessionId, recipient, now, home = profileDir()) {
41
+ let names;
42
+ try {
43
+ names = fs.readdirSync(pendingDir(home));
44
+ }
45
+ catch (error) {
46
+ if (error.code === 'ENOENT')
47
+ return undefined;
48
+ throw error;
49
+ }
50
+ for (const name of names.filter((value) => value.endsWith('.json')).sort()) {
51
+ const pending = readJson(path.join(pendingDir(home), name));
52
+ if (pending?.status === 'awaiting' && pending.sessionId === sessionId && pending.recipient === recipient && pending.courierDid === profile.courierDid
53
+ && pending.ownerDid === profile.ownerDid && Date.parse(pending.acceptanceExpiresAt) > now)
54
+ return pending;
55
+ }
56
+ return undefined;
57
+ }
58
+ /** A new pending invitation: fresh id, expiry at most 24 hours out and never past the main grant. */
59
+ export function newPendingInvite(profile, input, now) {
60
+ if (!profile.ownerDid || !profile.grantExpiresAt)
61
+ throw new CourierError('no-grant', 'No owner grant is installed.');
62
+ const expires = new Date(Math.floor(Math.min(now + DAY_MS, Date.parse(profile.grantExpiresAt)) / 1000) * 1000);
63
+ if (expires.getTime() - now < 60_000)
64
+ throw new CourierError('grant-expired', 'The Courier grant ends too soon to send an invitation.');
65
+ return {
66
+ v: 1, invitationId: ulid(now), recipient: input.recipient, sessionId: input.sessionId, subjectName: input.subjectName,
67
+ courierDid: profile.courierDid, ownerDid: profile.ownerDid, createdAt: new Date(now).toISOString(),
68
+ acceptanceExpiresAt: expires.toISOString(), accessExpiresAt: expires.toISOString(), status: 'awaiting',
69
+ };
70
+ }
71
+ export function invitationContext(pending) {
72
+ return {
73
+ kind: 'share-invitation', version: 1, invitationId: pending.invitationId, recipientEmail: pending.recipient,
74
+ subjectKind: 'session', subjectName: pending.subjectName,
75
+ acceptanceExpiresAt: pending.acceptanceExpiresAt, accessExpiresAt: pending.accessExpiresAt,
76
+ };
77
+ }
78
+ /** A host that could not be reached (or a registry that did not answer): says nothing about the approval, so it is retried, never refused. */
79
+ const TRANSIENT_ACTIVATION_CODES = new Set(['NODE_NOT_RESPONDING', 'registry_unavailable', 'ACTIVATION_TRANSPORT_FAILED']);
80
+ function activationUnavailable(error) {
81
+ const { reason, code } = (error ?? {});
82
+ return reason === 'activation_unavailable' || (typeof code === 'string' && TRANSIENT_ACTIVATION_CODES.has(code));
83
+ }
84
+ /** A share key id belongs to exactly one invitation, so a stored approval is never reusable for a second one. */
85
+ function keyIdUsedElsewhere(shareId, invitationId) {
86
+ let names;
87
+ try {
88
+ names = fs.readdirSync(grantDir());
89
+ }
90
+ catch (error) {
91
+ if (error.code === 'ENOENT')
92
+ return false;
93
+ throw error;
94
+ }
95
+ return names.filter((name) => name.endsWith('.json') && name !== `${invitationId}.json`)
96
+ .some((name) => readJson(path.join(grantDir(), name))?.shareId === shareId);
97
+ }
98
+ /**
99
+ * Verify the owner-signed grant for THIS pending request and install it as a runtime delegation on the Courier node. The
100
+ * grant's ReCap is a raw encryption network resource (no space), so it is a grant, not a session: the node-sdk verifies it
101
+ * (SIWE signature, signer, audience = this node's session key, expiry) and activates it. Order: the request binding, then
102
+ * the signed authority (exactly one key pair), then the install. Returns the key id from the signed grant.
103
+ */
104
+ async function activateInviteGrant(node, profile, pending, payload, now) {
105
+ const state = pending.transport;
106
+ if (typeof state?.requestId !== 'string' || typeof state.bodyCid !== 'string')
107
+ throw new CourierError('bad-grant', 'No approval request exists for this invitation.');
108
+ // One strict parse: the binding and the lifetime are read from it, and the signature is verified over its exact text.
109
+ const parsed = parseApprovalSiwe(payload.siwe);
110
+ assertApprovalRequestBinding(parsed, { requestId: state.requestId, bodyCid: state.bodyCid });
111
+ const times = { ...(parsed.issuedAt ? { issuedAt: parsed.issuedAt } : {}), ...(parsed.expiresAt ? { expiresAt: parsed.expiresAt } : {}) };
112
+ if (!times.expiresAt || times.expiresAt.getTime() <= now)
113
+ throw new CourierError('grant-expired', 'The invitation approval has expired.');
114
+ if (pending.courierDid !== profile.courierDid)
115
+ throw new CourierError('bad-grant', 'The invitation approval was not requested by this Courier key.');
116
+ const owner = `did:pkh:eip155:${payload.chainId}:${payload.address}`;
117
+ if (!profile.ownerDid || owner.toLowerCase() !== profile.ownerDid.toLowerCase() || owner.toLowerCase() !== pending.ownerDid.toLowerCase()) {
118
+ throw new CourierError('bad-grant', 'The invitation approval was given by a different account than the Courier owner.');
119
+ }
120
+ let verified;
121
+ try {
122
+ verified = node.verifySessionGrant({
123
+ delegationHeader: { Authorization: payload.delegationHeader.Authorization }, delegationCid: payload.delegationCid, spaceId: payload.spaceId,
124
+ address: payload.address, chainId: payload.chainId, siwe: parsed.raw, signature: payload.signature,
125
+ });
126
+ }
127
+ catch {
128
+ throw new CourierError('bad-grant', 'The invitation approval could not be verified for this Courier key.');
129
+ }
130
+ const signed = verified.recap.map((entry) => {
131
+ const service = entry.service.startsWith('tinycloud.') ? entry.service : `tinycloud.${entry.service}`;
132
+ return { service, space: entry.space, path: entry.path, actions: entry.actions.map((action) => (action.includes('/') ? action : `${service}/${action}`)), ...(entry.caveats?.length ? { caveats: [...entry.caveats] } : {}) };
133
+ });
134
+ const bounds = { ownerDid: owner, acceptanceExpiresAt: new Date(pending.acceptanceExpiresAt), now };
135
+ const shareId = assertInvitationApprovalGrant(signed, times, bounds);
136
+ const entry = signed[0];
137
+ const delegation = {
138
+ cid: payload.delegationCid, delegationHeader: { Authorization: payload.delegationHeader.Authorization }, spaceId: payload.spaceId,
139
+ path: entry.path, actions: [...entry.actions], resources: [{ service: 'encryption', space: 'encryption', path: entry.path, actions: [...entry.actions] }],
140
+ expiry: verified.expiresAt, delegateDID: payload.verificationMethod, ownerAddress: payload.address, chainId: payload.chainId, host: profile.host,
141
+ siweProof: { siwe: payload.siwe, signature: payload.signature },
142
+ };
143
+ try {
144
+ await activateValidatedRuntimeDelegation(node, delegation, {
145
+ host: profile.host,
146
+ // What the node ends up holding must be exactly the one pair, again from the signed bytes it parsed itself.
147
+ authorize: (effective) => { try {
148
+ return assertInvitationApprovalGrant(effective, times, bounds) === shareId;
149
+ }
150
+ catch {
151
+ return false;
152
+ } },
153
+ });
154
+ }
155
+ catch (error) {
156
+ // The host could not be reached (or answered with a transient status): the approval is fine and is kept; the next run installs it.
157
+ if (activationUnavailable(error)) {
158
+ throw new CourierError('activation-unavailable', 'The TinyCloud host could not be reached to install the invitation approval; nothing was sent. Run the command again to continue with the same approval.');
159
+ }
160
+ throw new CourierError('bad-grant', `The invitation approval could not be installed for this Courier key (${error.reason ?? error.code ?? 'refused'}).`);
161
+ }
162
+ return { shareId, expiresAt: verified.expiresAt };
163
+ }
164
+ /** A pending invitation whose approval cannot be used any more is closed, so the next run asks afresh. A stored approved grant is never removed. */
165
+ function closePending(pending) {
166
+ pending.status = 'closed';
167
+ savePendingInvite(pending);
168
+ }
169
+ /**
170
+ * Create (once) and await this invitation's approval, then keep the approved per-invite grant. Nothing the caller does
171
+ * after this - relay delegation, state, Share key, link or email - happens unless the result is `approved`.
172
+ */
173
+ export async function obtainInviteKey(options) {
174
+ const { node, profile, key, transport, pending } = options;
175
+ const clock = options.now ?? Date.now;
176
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
177
+ const stored = loadInviteGrant(pending.invitationId);
178
+ if (stored) {
179
+ if (stored.invitationId !== pending.invitationId)
180
+ throw new CourierError('bad-grant', 'This approval belongs to another invitation.');
181
+ const activated = await activateInviteGrant(node, profile, pending, stored.payload, clock());
182
+ if (activated.shareId !== stored.shareId)
183
+ throw new CourierError('bad-grant', 'The stored approval no longer matches its key.');
184
+ return { status: 'approved', shareId: stored.shareId };
185
+ }
186
+ if (Date.parse(pending.acceptanceExpiresAt) <= clock()) {
187
+ closePending(pending);
188
+ throw new CourierError('invite-expired', 'The approval for this invitation was not given in time; nothing was created.');
189
+ }
190
+ if (pending.transport === undefined) {
191
+ const created = await transport.create({ context: invitationContext(pending), key, host: profile.host });
192
+ pending.approvalUrl = created.approvalUrl;
193
+ pending.transport = created.transport;
194
+ savePendingInvite(pending);
195
+ }
196
+ options.onApprovalUrl?.(pending.approvalUrl, pending);
197
+ const deadline = clock() + options.waitMs;
198
+ for (;;) {
199
+ let result;
200
+ try {
201
+ result = await transport.poll(pending, key);
202
+ }
203
+ catch (error) {
204
+ // The approver says it does not know this request (or its result): nothing more can come of it.
205
+ if (error instanceof CourierError && error.code === 'approval-unavailable')
206
+ closePending(pending);
207
+ throw error;
208
+ }
209
+ savePendingInvite(pending);
210
+ if (result.status === 'approved') {
211
+ let activated;
212
+ try {
213
+ activated = await activateInviteGrant(node, profile, pending, result.payload, clock());
214
+ }
215
+ catch (error) {
216
+ // A result that is not this request's valid approval is never retried against the same request. A host that could not be reached
217
+ // says nothing about the approval, so the pending invitation stays and the result is fetched again on the next run.
218
+ if (error instanceof CourierError && error.code !== 'activation-unavailable')
219
+ closePending(pending);
220
+ throw error;
221
+ }
222
+ if (keyIdUsedElsewhere(activated.shareId, pending.invitationId)) {
223
+ closePending(pending);
224
+ throw new CourierError('bad-grant', 'This approval was already used for another invitation; each invitation needs its own approval.');
225
+ }
226
+ secureWrite(grantDir(), pending.invitationId, {
227
+ v: 1, invitationId: pending.invitationId, shareId: activated.shareId, payload: result.payload,
228
+ expiresAt: activated.expiresAt.toISOString(), receivedAt: new Date(clock()).toISOString(),
229
+ });
230
+ await transport.acknowledge?.(pending, key);
231
+ return { status: 'approved', shareId: activated.shareId };
232
+ }
233
+ if (result.status === 'denied' || result.status === 'expired') {
234
+ closePending(pending);
235
+ throw new CourierError('invite-approval-refused', result.status === 'denied' ? 'The owner declined this invitation; nothing was created.' : 'The approval request expired before it was given; nothing was created.');
236
+ }
237
+ if (clock() >= deadline)
238
+ return { status: 'awaiting', approvalUrl: pending.approvalUrl };
239
+ await sleep(Math.min(2000, Math.max(0, deadline - clock())));
240
+ }
241
+ }
242
+ export function closeInvite(pending) {
243
+ closePending(pending);
244
+ }
245
+ export function markInviteSent(pending) {
246
+ pending.status = 'sent';
247
+ savePendingInvite(pending);
248
+ }
249
+ /**
250
+ * Exactly one run may go on to create the relay, the Share key, the link and the email for an approved invitation:
251
+ * the marker is created exclusively and never removed. A run that finds it refuses, so a rerun or a concurrent run can
252
+ * never send a second email for the same approval. It also closes the pending invitation (the marker and any stored approved
253
+ * grant stay), so an attempt interrupted after the claim does not leave the invitation waiting forever: the next run asks the
254
+ * owner afresh, and the old approval is never used again.
255
+ */
256
+ export function claimInviteSend(pending) {
257
+ mkdirOwnerOnly(pendingDir());
258
+ try {
259
+ fs.closeSync(fs.openSync(path.join(pendingDir(), `${pending.invitationId}.sending`), 'wx', 0o600));
260
+ }
261
+ catch (error) {
262
+ if (error.code === 'EEXIST') {
263
+ // A concurrent run that already finished the send has recorded it as sent: that is never overwritten with closed.
264
+ if (loadPendingInvite(pending.invitationId)?.status !== 'sent')
265
+ closePending(pending);
266
+ throw new CourierError('invite-in-progress', 'This invitation was already sent or an earlier attempt did not finish; nothing more was sent. Run the command again to start a new invitation (a new owner approval).');
267
+ }
268
+ throw error;
269
+ }
270
+ }
271
+ //# sourceMappingURL=invite-approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invite-approval.js","sourceRoot":"","sources":["../src/invite-approval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { InviteApprovalTransport, PendingInvite } from './invite-approval.js';
2
+ import type { AgentKey } from './profile.js';
3
+ export interface OpenKeyInviteDeps {
4
+ fetch?: typeof fetch;
5
+ now?: () => number;
6
+ }
7
+ /**
8
+ * The OpenKey side of one invitation approval: a signed intent request, then repeatable requester-authenticated reads of
9
+ * its encrypted result. Result verification beyond the binding to this request (holder, owner, key, expiry) is done by
10
+ * the caller from the signed SIWE, never from anything the transport reports.
11
+ */
12
+ export declare function openKeyInviteTransport(deps?: OpenKeyInviteDeps): InviteApprovalTransport & {
13
+ acknowledge(pending: PendingInvite, key: AgentKey): Promise<void>;
14
+ };
@@ -0,0 +1,184 @@
1
+ import { createDecipheriv, createECDH, createHash, createHmac, createPrivateKey, randomBytes, randomUUID, sign } from 'node:crypto';
2
+ import { buildShareInvitationApprovalSpec, createApprovalRequest, signerFromJwk } from '@tinycloud/share-envelope/links';
3
+ import { CourierError } from './errors.js';
4
+ import { resolveOpenKeyApiHost } from './grant.js';
5
+ import { loadProfile } from './profile.js';
6
+ const DEFAULT_LINKS_API = 'https://api.tinycloud.xyz';
7
+ const unavailable = (message) => new CourierError('approval-unavailable', message);
8
+ function origin(value, label) {
9
+ let url;
10
+ try {
11
+ url = new URL(value);
12
+ }
13
+ catch {
14
+ throw unavailable(`${label} is not an origin.`);
15
+ }
16
+ const loopback = ['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname);
17
+ if (url.pathname !== '/' || url.search || url.hash || (url.protocol !== 'https:' && !(url.protocol === 'http:' && loopback)))
18
+ throw unavailable(`${label} must be an HTTPS origin.`);
19
+ return url.origin;
20
+ }
21
+ function sessionProof(state, key, action, resultRef, ackId) {
22
+ const jwk = key.jwk;
23
+ const jkt = createHash('sha256').update(JSON.stringify({ crv: 'Ed25519', kty: 'OKP', x: jwk.x })).digest('base64url');
24
+ const path = action === 'discover' ? `/api/approval-requests/${state.requestId}/result` : `/api/approval-results/${resultRef}/${action}`;
25
+ const header = Buffer.from(JSON.stringify({ alg: 'EdDSA', kid: jkt, typ: 'openkey-session-proof+jwt' })).toString('base64url');
26
+ const payload = Buffer.from(JSON.stringify({
27
+ client_id: 'openkey-approval-result-v1',
28
+ cred_hash: createHash('sha256').update(JSON.stringify([action, state.requestId, state.bodyCid, resultRef, ackId])).digest('base64url'),
29
+ htm: 'POST', htu: state.apiOrigin + path, iat: Math.floor(Date.now() / 1000), jti: randomBytes(24).toString('base64url'),
30
+ })).toString('base64url');
31
+ const input = `${header}.${payload}`;
32
+ const signature = sign(null, Buffer.from(input), createPrivateKey({ key: { kty: jwk.kty, crv: jwk.crv, x: jwk.x, d: jwk.d }, format: 'jwk' }));
33
+ return `${input}.${signature.toString('base64url')}`;
34
+ }
35
+ function canonicalB64(value) {
36
+ if (typeof value !== 'string' || !/^[A-Za-z0-9_-]+$/.test(value))
37
+ throw unavailable('OpenKey returned an invalid result.');
38
+ const decoded = Buffer.from(value, 'base64url');
39
+ if (decoded.toString('base64url') !== value)
40
+ throw unavailable('OpenKey returned an invalid result.');
41
+ return decoded;
42
+ }
43
+ /** The result is AES-GCM under an ECDH-P-256 key only this request's relay key can derive. Node's ECDH refuses off-curve points. */
44
+ function decryptResult(envelope, requestId, relay) {
45
+ const value = envelope;
46
+ if (!value || value.version !== 1 || value.algorithm !== 'ECDH-P256-A256GCM')
47
+ throw unavailable('OpenKey returned an unsupported result.');
48
+ const x = canonicalB64(value.ephemeralPublicJwk?.x);
49
+ const y = canonicalB64(value.ephemeralPublicJwk?.y);
50
+ const nonce = canonicalB64(value.nonce);
51
+ const ciphertext = canonicalB64(value.ciphertext);
52
+ if (x.length !== 32 || y.length !== 32 || nonce.length !== 12 || ciphertext.length <= 16)
53
+ throw unavailable('OpenKey returned an invalid result.');
54
+ let secret;
55
+ try {
56
+ secret = relay.computeSecret(Buffer.concat([Buffer.from([4]), x, y]));
57
+ }
58
+ catch {
59
+ throw unavailable('OpenKey returned an invalid result key.');
60
+ }
61
+ const extracted = createHmac('sha256', Buffer.from(requestId)).update(secret).digest();
62
+ const aesKey = createHmac('sha256', extracted).update(Buffer.from('openkey-device-relay-v1')).update(Buffer.from([1])).digest();
63
+ const decipher = createDecipheriv('aes-256-gcm', aesKey, nonce);
64
+ decipher.setAAD(Buffer.from(requestId));
65
+ decipher.setAuthTag(ciphertext.subarray(ciphertext.length - 16));
66
+ try {
67
+ const opened = JSON.parse(Buffer.concat([decipher.update(ciphertext.subarray(0, -16)), decipher.final()]).toString('utf8'));
68
+ if (opened && typeof opened === 'object' && !Array.isArray(opened))
69
+ return opened;
70
+ }
71
+ catch { /* falls through */ }
72
+ throw unavailable('OpenKey returned an unreadable result.');
73
+ }
74
+ /**
75
+ * The OpenKey side of one invitation approval: a signed intent request, then repeatable requester-authenticated reads of
76
+ * its encrypted result. Result verification beyond the binding to this request (holder, owner, key, expiry) is done by
77
+ * the caller from the signed SIWE, never from anything the transport reports.
78
+ */
79
+ export function openKeyInviteTransport(deps = {}) {
80
+ const fetchFn = deps.fetch ?? fetch;
81
+ const now = deps.now ?? Date.now;
82
+ const post = async (state, key, action) => {
83
+ const resultRef = action === 'discover' ? '' : state.resultRef ?? '';
84
+ const path = action === 'discover' ? `/api/approval-requests/${state.requestId}/result` : `/api/approval-results/${resultRef}/${action}`;
85
+ const body = action === 'discover'
86
+ ? { bodyCid: state.bodyCid, header: state.record.header, signature: state.record.signature }
87
+ : { requestId: state.requestId, bodyCid: state.bodyCid, ...(action === 'ack' ? { ackId: state.ackId } : {}) };
88
+ return fetchFn(state.apiOrigin + path, {
89
+ method: 'POST', credentials: 'omit', redirect: 'error', referrerPolicy: 'no-referrer',
90
+ headers: { 'content-type': 'application/json', 'OpenKey-Session-Proof': sessionProof(state, key, action, resultRef, action === 'ack' ? state.ackId : '') },
91
+ body: JSON.stringify(body), signal: AbortSignal.timeout(10_000),
92
+ });
93
+ };
94
+ return {
95
+ async create({ context, key, host }) {
96
+ const profile = loadProfile();
97
+ const openkeyOrigin = origin(profile.openkeyHost, 'OpenKey');
98
+ const apiOrigin = origin(resolveOpenKeyApiHost({ env: process.env.COURIER_OPENKEY_API_HOST, openkeyHost: openkeyOrigin }), 'OpenKey API');
99
+ const jwk = key.jwk;
100
+ const signer = signerFromJwk(jwk);
101
+ const spec = buildShareInvitationApprovalSpec({ targetDid: signer.did, targetPublicJwk: { kty: jwk.kty, crv: jwk.crv, x: jwk.x }, nodeOrigin: host, context, now: now() });
102
+ const relay = createECDH('prime256v1');
103
+ relay.generateKeys();
104
+ const point = relay.getPublicKey();
105
+ const request = { ...spec, resultEncryptionPublicJwk: { kty: 'EC', crv: 'P-256', x: point.subarray(1, 33).toString('base64url'), y: point.subarray(33).toString('base64url') } };
106
+ let created;
107
+ try {
108
+ created = await createApprovalRequest({ request, issuerKey: signer, apiOrigin: origin(process.env.COURIER_LINKS_API_ORIGIN ?? DEFAULT_LINKS_API, 'Links API'),
109
+ openkeyOrigin, expiresAt: new Date(context.acceptanceExpiresAt), now: now(), fetch: fetchFn });
110
+ }
111
+ catch {
112
+ throw unavailable('The approval request could not be created.');
113
+ }
114
+ const state = { requestId: created.id, bodyCid: created.bodyCid, record: created.record, request, apiOrigin,
115
+ relayPrivateKey: relay.getPrivateKey().toString('base64url'), ackId: randomUUID() };
116
+ return { approvalUrl: created.link, transport: state };
117
+ },
118
+ async poll(pending, key) {
119
+ const state = pending.transport;
120
+ if (!state)
121
+ throw unavailable('No approval request exists for this invitation.');
122
+ if (!state.resultEnvelope) {
123
+ let response;
124
+ try {
125
+ response = await post(state, key, 'discover');
126
+ }
127
+ catch {
128
+ return { status: 'pending' };
129
+ }
130
+ if (response.status === 202)
131
+ return { status: 'pending' };
132
+ if (!response.ok) {
133
+ const failed = (await response.clone().json().catch(() => null));
134
+ const code = failed?.code;
135
+ if (failed?.status === 'denied')
136
+ return { status: 'denied' };
137
+ if (code === 'expired_request' || code === 'expired_result')
138
+ return { status: 'expired' };
139
+ if (code === 'not_found' || code === 'request_binding' || code === 'invalid_proof')
140
+ throw unavailable('OpenKey does not recognise this approval request.');
141
+ return { status: 'pending' };
142
+ }
143
+ const found = await response.json();
144
+ if (typeof found.resultRef !== 'string' || !/^[A-Za-z0-9_-]{20,128}$/.test(found.resultRef) || typeof found.expiresAt !== 'string')
145
+ throw unavailable('OpenKey returned an invalid result reference.');
146
+ state.resultRef = found.resultRef;
147
+ state.resultExpiresAt = found.expiresAt;
148
+ let fetched;
149
+ try {
150
+ fetched = await post(state, key, 'fetch');
151
+ }
152
+ catch {
153
+ return { status: 'pending' };
154
+ }
155
+ if (!fetched.ok)
156
+ return { status: 'pending' };
157
+ const result = await fetched.json();
158
+ if (result.requestId !== state.requestId || result.bodyCid !== state.bodyCid || result.resultRef !== state.resultRef || result.expiresAt !== state.resultExpiresAt || !result.relay) {
159
+ throw unavailable('The approval result does not match this request.');
160
+ }
161
+ state.resultEnvelope = result.relay;
162
+ }
163
+ const relay = createECDH('prime256v1');
164
+ relay.setPrivateKey(Buffer.from(state.relayPrivateKey, 'base64url'));
165
+ const opened = decryptResult(state.resultEnvelope, state.requestId, relay);
166
+ const delegation = opened.delegation;
167
+ if (opened.resultVersion !== 1 || opened.requestId !== state.requestId || opened.requestDigest !== state.bodyCid
168
+ || opened.requesterDid !== state.request.issuer || opened.targetDid !== state.request.targetDid || !delegation || typeof delegation !== 'object') {
169
+ throw new CourierError('bad-grant', 'The approval result does not belong to this request.');
170
+ }
171
+ return { status: 'approved', payload: delegation };
172
+ },
173
+ async acknowledge(pending, key) {
174
+ const state = pending.transport;
175
+ if (!state?.resultRef)
176
+ return;
177
+ try {
178
+ await post(state, key, 'ack');
179
+ }
180
+ catch { /* the stored approval is already committed; OpenKey may be told on the next run */ }
181
+ },
182
+ };
183
+ }
184
+ //# sourceMappingURL=invite-openkey.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invite-openkey.js","sourceRoot":"","sources":["../src/invite-openkey.ts"],"names":[],"mappings":"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"}
package/dist/main.js CHANGED
@@ -52,7 +52,7 @@ Setup & pairing (humans run these):
52
52
  Accept artifact A, B or C; prints B or C to pass on
53
53
  pair renew [peer] [--force] Renew both directions; humans or watcher only
54
54
  pair renew <peer> --renew-artifact [--force] Emit renewed C artifact for handoff; receiver runs pair accept
55
- revoke <peer> Stop renewing + drop the peer (access lapses at delegation expiry)
55
+ revoke <peer> Stop renewing and remove the peer; their access ends when the current grant expires. Files you already shared stay available until each share expires or you revoke it (\`tinycloud share revoke <id>\`).
56
56
 
57
57
  Threads (agents run these):
58
58
  send <peer> (--new <subject> | --thread <id>) [--text t | --file f | stdin]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinycloud/courier",
3
- "version": "0.6.0-beta.18",
3
+ "version": "0.6.0-beta.20",
4
4
  "description": "Courier: agent-to-agent threads over TinyCloud (CLI, library and agent skill)",
5
5
  "author": "TinyCloud, Inc.",
6
6
  "type": "module",
@@ -30,10 +30,10 @@
30
30
  "test:live": "tsc -p tsconfig.json && node scripts/happy-path.mjs"
31
31
  },
32
32
  "dependencies": {
33
- "@tinycloud/node-sdk": "3.12.0-beta.16",
34
- "@tinycloud/sdk-core": "3.12.0-beta.16",
35
- "@tinycloud/share-envelope": "1.5.0",
36
- "@tinycloud/share-sdk": "1.5.0-beta.0",
33
+ "@tinycloud/node-sdk": "3.12.0-beta.18",
34
+ "@tinycloud/sdk-core": "3.12.0-beta.18",
35
+ "@tinycloud/share-envelope": "1.6.0-beta.0",
36
+ "@tinycloud/share-sdk": "1.5.0-beta.2",
37
37
  "ulid": "^3.0.2"
38
38
  },
39
39
  "devDependencies": {