crewly 1.20.303 → 1.20.304
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/config/skills/orchestrator/move-team/SKILL.md +53 -0
- package/config/skills/orchestrator/move-team/execute.sh +42 -0
- package/dist/backend/backend/src/constants.d.ts +69 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +69 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts +45 -0
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.js +214 -0
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +22 -3
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/backend/backend/src/services/core/storage.service.d.ts +18 -0
- package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/storage.service.js +16 -0
- package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts +19 -0
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.js +46 -0
- package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +25 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +41 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts +122 -0
- package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-archive.js +202 -0
- package/dist/backend/backend/src/services/team-move/team-move-archive.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts +59 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.js +133 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts +102 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js +217 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts +102 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.js +348 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts +209 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.js +504 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts +156 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.js +462 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts +157 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.js +298 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.d.ts +69 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.js +198 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts +121 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.js +462 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts +110 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.js +198 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts +71 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.js +102 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts +29 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.js +173 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts +50 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js +96 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.d.ts +94 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.js +124 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts +46 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.js +202 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.d.ts +269 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.js +44 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +69 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +69 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.d.ts +18 -0
- package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.js +16 -0
- package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts +19 -0
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.js +46 -0
- package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +25 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +41 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/cli/src/commands/team-move.d.ts +68 -0
- package/dist/cli/cli/src/commands/team-move.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/team-move.js +155 -0
- package/dist/cli/cli/src/commands/team-move.js.map +1 -0
- package/dist/cli/cli/src/index.js +33 -0
- package/dist/cli/cli/src/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team move crypto — end-to-end encryption of the package stream.
|
|
3
|
+
*
|
|
4
|
+
* Only the two machines can read a package; Crewly Cloud stores ciphertext.
|
|
5
|
+
*
|
|
6
|
+
* - The TARGET makes an ephemeral X25519 key pair per move. The public key
|
|
7
|
+
* goes to Cloud, the private key never leaves the target's
|
|
8
|
+
* `~/.crewly/team-moves/<moveId>/`.
|
|
9
|
+
* - The SOURCE makes an ephemeral X25519 key pair per package, derives a
|
|
10
|
+
* shared secret with the target's public key and runs it through
|
|
11
|
+
* HKDF-SHA256 (random 32-byte salt, `info` binds the move id) to get a
|
|
12
|
+
* one-time AES-256 key.
|
|
13
|
+
* - The stream is cut into frames of `frameBytes` plaintext. Each frame is
|
|
14
|
+
* sealed with AES-256-GCM under its own nonce (frame counter). The
|
|
15
|
+
* authenticated data of every frame is the header (which carries the move
|
|
16
|
+
* id and the ephemeral public key), the frame counter and a "final" flag,
|
|
17
|
+
* so a frame cannot be moved, dropped, repeated or cut off at the end, and
|
|
18
|
+
* a package cannot be replayed into another move, without failing.
|
|
19
|
+
*
|
|
20
|
+
* Wire format:
|
|
21
|
+
*
|
|
22
|
+
* header : "CTMV" | version u8 | frameBytes u32 | ephemeralPublicKey[32]
|
|
23
|
+
* | salt[32] | moveIdLength u8 | moveId
|
|
24
|
+
* frame* : length u32 (ciphertext incl. 16-byte tag) | ciphertext
|
|
25
|
+
*
|
|
26
|
+
* The one-time key makes counter nonces safe: a (key, nonce) pair is never
|
|
27
|
+
* used twice. Both transforms are streaming — memory stays at one frame.
|
|
28
|
+
*
|
|
29
|
+
* @module services/team-move/team-move-crypto
|
|
30
|
+
*/
|
|
31
|
+
import { createCipheriv, createDecipheriv, createHash, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, } from 'node:crypto';
|
|
32
|
+
import { Transform } from 'node:stream';
|
|
33
|
+
import { TEAM_MOVE_CONSTANTS } from '../../constants.js';
|
|
34
|
+
/** Magic bytes at the start of an encrypted package. */
|
|
35
|
+
const MAGIC = Buffer.from('CTMV', 'ascii');
|
|
36
|
+
/** Format version in the header. */
|
|
37
|
+
const FORMAT_VERSION = 1;
|
|
38
|
+
/** Length of the GCM tag. */
|
|
39
|
+
const TAG_BYTES = 16;
|
|
40
|
+
/** Length of an X25519 raw key. */
|
|
41
|
+
const KEY_BYTES = 32;
|
|
42
|
+
/** DER prefix of an X25519 SubjectPublicKeyInfo (the 32 key bytes follow). */
|
|
43
|
+
const X25519_SPKI_PREFIX = Buffer.from('302a300506032b656e032100', 'hex');
|
|
44
|
+
/** HKDF info prefix; the move id is appended. */
|
|
45
|
+
const HKDF_INFO_PREFIX = 'crewly-team-move/v1|';
|
|
46
|
+
/** Largest frame size a header may announce (guards memory). */
|
|
47
|
+
const MAX_FRAME_BYTES = 16 * 1024 * 1024;
|
|
48
|
+
/** Fixed part of the header, before the move id. */
|
|
49
|
+
const FIXED_HEADER_BYTES = MAGIC.length + 1 + 4 + KEY_BYTES + KEY_BYTES + 1;
|
|
50
|
+
/** Thrown when a package cannot be decrypted or has been altered. */
|
|
51
|
+
export class TeamMoveCryptoError extends Error {
|
|
52
|
+
constructor(message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = 'TeamMoveCryptoError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Make the target's ephemeral key pair for one move.
|
|
59
|
+
*
|
|
60
|
+
* @returns Base64 public (raw) and private (PKCS8) keys
|
|
61
|
+
*/
|
|
62
|
+
export function generateTargetKeyPair() {
|
|
63
|
+
const { publicKey, privateKey } = generateKeyPairSync('x25519');
|
|
64
|
+
return {
|
|
65
|
+
publicKey: rawPublicKey(publicKey).toString('base64'),
|
|
66
|
+
privateKey: privateKey.export({ format: 'der', type: 'pkcs8' }).toString('base64'),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Raw 32 public-key bytes of a key object.
|
|
71
|
+
*
|
|
72
|
+
* @param key - Public or private X25519 key
|
|
73
|
+
* @returns The 32 raw bytes
|
|
74
|
+
*/
|
|
75
|
+
function rawPublicKey(key) {
|
|
76
|
+
const spki = (key.type === 'private' ? createPublicKey(key) : key).export({ format: 'der', type: 'spki' });
|
|
77
|
+
return Buffer.from(spki.subarray(spki.length - KEY_BYTES));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Import a raw X25519 public key.
|
|
81
|
+
*
|
|
82
|
+
* @param raw - 32 raw bytes
|
|
83
|
+
* @returns Key object
|
|
84
|
+
*/
|
|
85
|
+
function importPublicKey(raw) {
|
|
86
|
+
if (raw.length !== KEY_BYTES)
|
|
87
|
+
throw new TeamMoveCryptoError('public key must be 32 bytes');
|
|
88
|
+
return createPublicKey({ key: Buffer.concat([X25519_SPKI_PREFIX, raw]), format: 'der', type: 'spki' });
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether a string is a base64 raw X25519 public key (as Cloud validates it).
|
|
92
|
+
*
|
|
93
|
+
* @param value - Candidate
|
|
94
|
+
* @returns True for 32 bytes of base64
|
|
95
|
+
*/
|
|
96
|
+
export function isPublicKeyString(value) {
|
|
97
|
+
return typeof value === 'string' && /^[A-Za-z0-9+/]{43}=$/.test(value) && Buffer.from(value, 'base64').length === KEY_BYTES;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* One-time AES key for a package.
|
|
101
|
+
*
|
|
102
|
+
* @param privateKey - One side's X25519 private key
|
|
103
|
+
* @param peerPublic - The other side's X25519 public key
|
|
104
|
+
* @param salt - 32 random bytes from the header
|
|
105
|
+
* @param moveId - Move id (bound into the derivation)
|
|
106
|
+
* @returns 32-byte key
|
|
107
|
+
*/
|
|
108
|
+
function deriveKey(privateKey, peerPublic, salt, moveId) {
|
|
109
|
+
const shared = diffieHellman({ privateKey, publicKey: peerPublic });
|
|
110
|
+
return Buffer.from(hkdfSync('sha256', shared, salt, Buffer.from(HKDF_INFO_PREFIX + moveId, 'utf8'), 32));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Nonce of frame `counter`: 4 zero bytes + 8-byte big-endian counter.
|
|
114
|
+
*
|
|
115
|
+
* @param counter - Frame index
|
|
116
|
+
* @returns 12-byte nonce
|
|
117
|
+
*/
|
|
118
|
+
function nonceFor(counter) {
|
|
119
|
+
const nonce = Buffer.alloc(12);
|
|
120
|
+
nonce.writeBigUInt64BE(BigInt(counter), 4);
|
|
121
|
+
return nonce;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Authenticated data of a frame: header, frame index, final flag.
|
|
125
|
+
*
|
|
126
|
+
* @param header - Package header bytes
|
|
127
|
+
* @param counter - Frame index
|
|
128
|
+
* @param final - Whether this is the last frame
|
|
129
|
+
* @returns AAD bytes
|
|
130
|
+
*/
|
|
131
|
+
function aadFor(header, counter, final) {
|
|
132
|
+
const tail = Buffer.alloc(9);
|
|
133
|
+
tail.writeBigUInt64BE(BigInt(counter), 0);
|
|
134
|
+
tail.writeUInt8(final ? 1 : 0, 8);
|
|
135
|
+
return Buffer.concat([header, tail]);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Transform that encrypts a byte stream for the target of a move.
|
|
139
|
+
*
|
|
140
|
+
* @param options - Recipient key, move id, frame size
|
|
141
|
+
* @returns A Transform (plaintext in, package out)
|
|
142
|
+
* @throws TeamMoveCryptoError for an invalid recipient key or move id
|
|
143
|
+
*/
|
|
144
|
+
export function createEncryptStream(options) {
|
|
145
|
+
const frameBytes = options.frameBytes ?? TEAM_MOVE_CONSTANTS.CRYPTO_FRAME_BYTES;
|
|
146
|
+
if (!Number.isInteger(frameBytes) || frameBytes <= 0 || frameBytes > MAX_FRAME_BYTES) {
|
|
147
|
+
throw new TeamMoveCryptoError('invalid frame size');
|
|
148
|
+
}
|
|
149
|
+
const moveId = Buffer.from(options.moveId, 'utf8');
|
|
150
|
+
if (moveId.length === 0 || moveId.length > 255)
|
|
151
|
+
throw new TeamMoveCryptoError('invalid move id');
|
|
152
|
+
if (!isPublicKeyString(options.recipientPublicKey))
|
|
153
|
+
throw new TeamMoveCryptoError('invalid recipient public key');
|
|
154
|
+
const eph = generateKeyPairSync('x25519');
|
|
155
|
+
const salt = randomBytes(KEY_BYTES);
|
|
156
|
+
const key = deriveKey(eph.privateKey, importPublicKey(Buffer.from(options.recipientPublicKey, 'base64')), salt, options.moveId);
|
|
157
|
+
const fixed = Buffer.alloc(MAGIC.length + 1 + 4);
|
|
158
|
+
MAGIC.copy(fixed, 0);
|
|
159
|
+
fixed.writeUInt8(FORMAT_VERSION, MAGIC.length);
|
|
160
|
+
fixed.writeUInt32BE(frameBytes, MAGIC.length + 1);
|
|
161
|
+
const header = Buffer.concat([fixed, rawPublicKey(eph.publicKey), salt, Buffer.from([moveId.length]), moveId]);
|
|
162
|
+
let pending = [];
|
|
163
|
+
let pendingBytes = 0;
|
|
164
|
+
let counter = 0;
|
|
165
|
+
let headerSent = false;
|
|
166
|
+
const seal = (plain, final) => {
|
|
167
|
+
const cipher = createCipheriv('aes-256-gcm', key, nonceFor(counter), { authTagLength: TAG_BYTES });
|
|
168
|
+
cipher.setAAD(aadFor(header, counter, final));
|
|
169
|
+
const body = Buffer.concat([cipher.update(plain), cipher.final(), cipher.getAuthTag()]);
|
|
170
|
+
counter += 1;
|
|
171
|
+
const len = Buffer.alloc(4);
|
|
172
|
+
len.writeUInt32BE(body.length, 0);
|
|
173
|
+
return Buffer.concat([len, body]);
|
|
174
|
+
};
|
|
175
|
+
return new Transform({
|
|
176
|
+
transform(chunk, _encoding, callback) {
|
|
177
|
+
try {
|
|
178
|
+
if (!headerSent) {
|
|
179
|
+
this.push(header);
|
|
180
|
+
headerSent = true;
|
|
181
|
+
}
|
|
182
|
+
pending.push(chunk);
|
|
183
|
+
pendingBytes += chunk.length;
|
|
184
|
+
// Keep at least one byte back so the last frame can be sealed as final.
|
|
185
|
+
while (pendingBytes > frameBytes) {
|
|
186
|
+
const all = Buffer.concat(pending);
|
|
187
|
+
this.push(seal(all.subarray(0, frameBytes), false));
|
|
188
|
+
const rest = all.subarray(frameBytes);
|
|
189
|
+
pending = rest.length > 0 ? [rest] : [];
|
|
190
|
+
pendingBytes = rest.length;
|
|
191
|
+
}
|
|
192
|
+
callback();
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
callback(error);
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
flush(callback) {
|
|
199
|
+
try {
|
|
200
|
+
if (!headerSent) {
|
|
201
|
+
this.push(header);
|
|
202
|
+
headerSent = true;
|
|
203
|
+
}
|
|
204
|
+
this.push(seal(Buffer.concat(pending), true));
|
|
205
|
+
callback();
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
callback(error);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Transform that decrypts a package on the target. Fails with
|
|
215
|
+
* {@link TeamMoveCryptoError} on any altered, truncated, reordered or
|
|
216
|
+
* foreign-move package.
|
|
217
|
+
*
|
|
218
|
+
* @param options - Target private key and expected move id
|
|
219
|
+
* @returns A Transform (package in, plaintext out)
|
|
220
|
+
*/
|
|
221
|
+
export function createDecryptStream(options) {
|
|
222
|
+
let privateKey;
|
|
223
|
+
try {
|
|
224
|
+
privateKey = createPrivateKey({ key: Buffer.from(options.privateKey, 'base64'), format: 'der', type: 'pkcs8' });
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
throw new TeamMoveCryptoError('invalid private key');
|
|
228
|
+
}
|
|
229
|
+
let buffer = Buffer.alloc(0);
|
|
230
|
+
let header = null;
|
|
231
|
+
let key = null;
|
|
232
|
+
let frameBytes = 0;
|
|
233
|
+
let counter = 0;
|
|
234
|
+
const open = (body, final) => {
|
|
235
|
+
if (!header || !key)
|
|
236
|
+
throw new TeamMoveCryptoError('package header missing');
|
|
237
|
+
if (body.length < TAG_BYTES)
|
|
238
|
+
throw new TeamMoveCryptoError('truncated frame');
|
|
239
|
+
const decipher = createDecipheriv('aes-256-gcm', key, nonceFor(counter), { authTagLength: TAG_BYTES });
|
|
240
|
+
decipher.setAAD(aadFor(header, counter, final));
|
|
241
|
+
decipher.setAuthTag(body.subarray(body.length - TAG_BYTES));
|
|
242
|
+
try {
|
|
243
|
+
const plain = Buffer.concat([decipher.update(body.subarray(0, body.length - TAG_BYTES)), decipher.final()]);
|
|
244
|
+
counter += 1;
|
|
245
|
+
return plain;
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
throw new TeamMoveCryptoError('package failed authentication (altered, truncated or not for this move)');
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
/** Parse the header once enough bytes are here; returns false when more are needed. */
|
|
252
|
+
const readHeader = () => {
|
|
253
|
+
if (buffer.length < FIXED_HEADER_BYTES)
|
|
254
|
+
return false;
|
|
255
|
+
if (!buffer.subarray(0, MAGIC.length).equals(MAGIC))
|
|
256
|
+
throw new TeamMoveCryptoError('not a team move package');
|
|
257
|
+
if (buffer.readUInt8(MAGIC.length) !== FORMAT_VERSION)
|
|
258
|
+
throw new TeamMoveCryptoError('unsupported package version');
|
|
259
|
+
const announced = buffer.readUInt32BE(MAGIC.length + 1);
|
|
260
|
+
if (announced <= 0 || announced > MAX_FRAME_BYTES)
|
|
261
|
+
throw new TeamMoveCryptoError('invalid frame size in package');
|
|
262
|
+
const idLength = buffer.readUInt8(FIXED_HEADER_BYTES - 1);
|
|
263
|
+
const total = FIXED_HEADER_BYTES + idLength;
|
|
264
|
+
if (buffer.length < total)
|
|
265
|
+
return false;
|
|
266
|
+
const moveId = buffer.subarray(FIXED_HEADER_BYTES, total).toString('utf8');
|
|
267
|
+
if (moveId !== options.moveId)
|
|
268
|
+
throw new TeamMoveCryptoError('package belongs to another move');
|
|
269
|
+
const ephPublic = buffer.subarray(MAGIC.length + 5, MAGIC.length + 5 + KEY_BYTES);
|
|
270
|
+
const salt = buffer.subarray(MAGIC.length + 5 + KEY_BYTES, MAGIC.length + 5 + 2 * KEY_BYTES);
|
|
271
|
+
header = Buffer.from(buffer.subarray(0, total));
|
|
272
|
+
key = deriveKey(privateKey, importPublicKey(Buffer.from(ephPublic)), Buffer.from(salt), moveId);
|
|
273
|
+
frameBytes = announced;
|
|
274
|
+
buffer = buffer.subarray(total);
|
|
275
|
+
return true;
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* Open every frame that is certainly not the last one: a frame is open-able
|
|
279
|
+
* once at least one more byte follows it.
|
|
280
|
+
*/
|
|
281
|
+
const drain = (push) => {
|
|
282
|
+
for (;;) {
|
|
283
|
+
if (!header && !readHeader())
|
|
284
|
+
return;
|
|
285
|
+
if (buffer.length < 4)
|
|
286
|
+
return;
|
|
287
|
+
const length = buffer.readUInt32BE(0);
|
|
288
|
+
if (length < TAG_BYTES || length > frameBytes + TAG_BYTES)
|
|
289
|
+
throw new TeamMoveCryptoError('invalid frame length');
|
|
290
|
+
if (buffer.length < 4 + length + 1)
|
|
291
|
+
return; // need a byte beyond it to know it is not final
|
|
292
|
+
const body = buffer.subarray(4, 4 + length);
|
|
293
|
+
push(open(body, false));
|
|
294
|
+
buffer = buffer.subarray(4 + length);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
return new Transform({
|
|
298
|
+
transform(chunk, _encoding, callback) {
|
|
299
|
+
try {
|
|
300
|
+
buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);
|
|
301
|
+
drain((b) => this.push(b));
|
|
302
|
+
callback();
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
callback(error);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
flush(callback) {
|
|
309
|
+
try {
|
|
310
|
+
if (!header && !readHeader())
|
|
311
|
+
throw new TeamMoveCryptoError('package is empty or truncated');
|
|
312
|
+
if (buffer.length < 4)
|
|
313
|
+
throw new TeamMoveCryptoError('package is truncated');
|
|
314
|
+
const length = buffer.readUInt32BE(0);
|
|
315
|
+
if (length < TAG_BYTES || length > frameBytes + TAG_BYTES)
|
|
316
|
+
throw new TeamMoveCryptoError('invalid frame length');
|
|
317
|
+
if (buffer.length !== 4 + length)
|
|
318
|
+
throw new TeamMoveCryptoError('package is truncated or has trailing data');
|
|
319
|
+
this.push(open(buffer.subarray(4), true));
|
|
320
|
+
buffer = Buffer.alloc(0);
|
|
321
|
+
callback();
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
callback(error);
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Pass-through stream computing SHA-256 and size of its data.
|
|
331
|
+
*
|
|
332
|
+
* @returns The tap
|
|
333
|
+
*/
|
|
334
|
+
export function createHashTap() {
|
|
335
|
+
const hash = createHash('sha256');
|
|
336
|
+
let bytes = 0;
|
|
337
|
+
const tap = new Transform({
|
|
338
|
+
transform(chunk, _encoding, callback) {
|
|
339
|
+
hash.update(chunk);
|
|
340
|
+
bytes += chunk.length;
|
|
341
|
+
callback(null, chunk);
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
Object.defineProperty(tap, 'bytes', { get: () => bytes });
|
|
345
|
+
tap.digest = () => hash.digest('hex');
|
|
346
|
+
return tap;
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=team-move-crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-move-crypto.js","sourceRoot":"","sources":["../../../../../../backend/src/services/team-move/team-move-crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team move driver — the machine side of the Cloud protocol.
|
|
3
|
+
*
|
|
4
|
+
* Cloud tells a machine what to do with a `team_move` relay push
|
|
5
|
+
* (`{moveId, role, action}`); the push is only a hint, the driver fetches the
|
|
6
|
+
* move from Cloud and acts on what Cloud says. The same list is read at boot
|
|
7
|
+
* and every few minutes, so a machine that was offline when the push went out
|
|
8
|
+
* picks the move up when it returns. Every action is idempotent (journaled
|
|
9
|
+
* steps, resumable upload/download), so a repeated push or a restart in the
|
|
10
|
+
* middle is harmless.
|
|
11
|
+
*
|
|
12
|
+
* target prepare -> make the per-move key, preflight, publish the key
|
|
13
|
+
* source package -> collect, scan, encrypt, upload
|
|
14
|
+
* target import -> download, decrypt, import, start, report `imported`
|
|
15
|
+
* source stop_archive -> stop the team, archive it, report `source_stopped`
|
|
16
|
+
* either rollback -> source: restore + start (`source_restored`);
|
|
17
|
+
* target: stop + archive its copy (`target_stopped`).
|
|
18
|
+
* Also "abort and clean up" after a failure.
|
|
19
|
+
*
|
|
20
|
+
* Starting the driver advertises the `teamMove:1` capability to Cloud.
|
|
21
|
+
*
|
|
22
|
+
* @module services/team-move/team-move-driver.service
|
|
23
|
+
*/
|
|
24
|
+
import { type ComponentLogger } from '../core/logger.service.js';
|
|
25
|
+
import type { IncomingMessage } from './../cloud/cloud-sync.types.js';
|
|
26
|
+
import { TeamMoveArchiver } from './team-move-archive.js';
|
|
27
|
+
import { TeamMoveCloudClient } from './team-move-cloud.client.js';
|
|
28
|
+
import { TeamMoveExporter } from './team-move-exporter.js';
|
|
29
|
+
import { TeamMoveImporter } from './team-move-importer.js';
|
|
30
|
+
import type { PackageRepo, TeamMovePreflight, TeamMoveRelayData, TeamMoveView } from './team-move.types.js';
|
|
31
|
+
/**
|
|
32
|
+
* Install the process-wide driver (composition root).
|
|
33
|
+
*
|
|
34
|
+
* @param driver - The driver or null
|
|
35
|
+
*/
|
|
36
|
+
export declare function setTeamMoveDriver(driver: TeamMoveDriver | null): void;
|
|
37
|
+
/**
|
|
38
|
+
* The process-wide driver, or null before boot wired it.
|
|
39
|
+
*
|
|
40
|
+
* @returns The driver or null
|
|
41
|
+
*/
|
|
42
|
+
export declare function getTeamMoveDriver(): TeamMoveDriver | null;
|
|
43
|
+
/**
|
|
44
|
+
* Whether the driver runs on this machine.
|
|
45
|
+
*
|
|
46
|
+
* @returns True once started
|
|
47
|
+
*/
|
|
48
|
+
export declare function isTeamMoveDriverActive(): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Capabilities to advertise to Cloud (heartbeat).
|
|
51
|
+
*
|
|
52
|
+
* @returns `['teamMove:1']` while the driver runs, else `[]`
|
|
53
|
+
*/
|
|
54
|
+
export declare function teamMoveCapabilities(): string[];
|
|
55
|
+
/** Emitter of relay messages (CloudSyncService). */
|
|
56
|
+
export interface TeamMoveRelaySource {
|
|
57
|
+
on(event: 'message', listener: (msg: IncomingMessage) => void): unknown;
|
|
58
|
+
off(event: 'message', listener: (msg: IncomingMessage) => void): unknown;
|
|
59
|
+
}
|
|
60
|
+
/** Collaborators of the driver. */
|
|
61
|
+
export interface TeamMoveDriverDeps {
|
|
62
|
+
home?: string;
|
|
63
|
+
cloud: TeamMoveCloudClient;
|
|
64
|
+
source: TeamMoveRelaySource;
|
|
65
|
+
identity: () => Promise<{
|
|
66
|
+
instanceId: string;
|
|
67
|
+
deviceName: string;
|
|
68
|
+
}>;
|
|
69
|
+
exporter: TeamMoveExporter;
|
|
70
|
+
/** Built per import so each uses the current services. */
|
|
71
|
+
importer: () => TeamMoveImporter;
|
|
72
|
+
archiver: TeamMoveArchiver;
|
|
73
|
+
preflight: (requiredTools?: readonly string[]) => Promise<TeamMovePreflight>;
|
|
74
|
+
/** True when no member of the team is working. */
|
|
75
|
+
isTeamIdle: (teamId: string) => Promise<boolean>;
|
|
76
|
+
sleep?: (ms: number) => Promise<void>;
|
|
77
|
+
now?: () => string;
|
|
78
|
+
logger?: ComponentLogger;
|
|
79
|
+
}
|
|
80
|
+
/** The driver. */
|
|
81
|
+
export declare class TeamMoveDriver {
|
|
82
|
+
private readonly deps;
|
|
83
|
+
private readonly logger;
|
|
84
|
+
private readonly home;
|
|
85
|
+
private readonly sleep;
|
|
86
|
+
private readonly now;
|
|
87
|
+
private readonly inFlight;
|
|
88
|
+
private readonly listener;
|
|
89
|
+
private pollTimer;
|
|
90
|
+
private bootTimer;
|
|
91
|
+
private started;
|
|
92
|
+
constructor(deps: TeamMoveDriverDeps);
|
|
93
|
+
/** Listen for pushes, resume pending moves, advertise the capability. Idempotent. */
|
|
94
|
+
start(): void;
|
|
95
|
+
/** Stop listening; the capability is no longer advertised. */
|
|
96
|
+
stop(): void;
|
|
97
|
+
/**
|
|
98
|
+
* Handle one relay message. Anything but a well-formed `team_move` is ignored.
|
|
99
|
+
*
|
|
100
|
+
* @param msg - Relay message
|
|
101
|
+
* @returns True when it was a team move push that started work
|
|
102
|
+
*/
|
|
103
|
+
handleRelay(msg: IncomingMessage): Promise<boolean>;
|
|
104
|
+
/** Read this machine's pending moves from Cloud and act on each. */
|
|
105
|
+
poll(): Promise<number>;
|
|
106
|
+
/** Run one action (one at a time per move and action). */
|
|
107
|
+
private dispatch;
|
|
108
|
+
private execute;
|
|
109
|
+
private prepare;
|
|
110
|
+
private packageAndUpload;
|
|
111
|
+
private waitForIdle;
|
|
112
|
+
private importPackage;
|
|
113
|
+
private stopArchive;
|
|
114
|
+
private rollback;
|
|
115
|
+
/**
|
|
116
|
+
* Ask Cloud to move a team from this machine to another.
|
|
117
|
+
*
|
|
118
|
+
* @param input - Team, target machine, flags
|
|
119
|
+
* @returns The new move id
|
|
120
|
+
* @throws TeamMoveBlockedError when a repository holds unpushed work (unless allowed)
|
|
121
|
+
*/
|
|
122
|
+
startMove(input: {
|
|
123
|
+
teamId: string;
|
|
124
|
+
targetInstanceId: string;
|
|
125
|
+
allowDirty?: boolean;
|
|
126
|
+
skipFlagged?: boolean;
|
|
127
|
+
}): Promise<{
|
|
128
|
+
moveId: string;
|
|
129
|
+
}>;
|
|
130
|
+
/**
|
|
131
|
+
* What would stop a move of a team: repositories with unpushed or uncommitted work.
|
|
132
|
+
*
|
|
133
|
+
* @param teamId - Team id
|
|
134
|
+
* @returns Repo states and the blocking lines
|
|
135
|
+
*/
|
|
136
|
+
checkRepos(teamId: string): Promise<{
|
|
137
|
+
repos: PackageRepo[];
|
|
138
|
+
blocking: string[];
|
|
139
|
+
}>;
|
|
140
|
+
/**
|
|
141
|
+
* Dry run: build the package here exactly as a move would, encrypt it with a
|
|
142
|
+
* throwaway key to measure it, and report. Nothing is sent anywhere.
|
|
143
|
+
*
|
|
144
|
+
* @param input - Team and a scratch directory
|
|
145
|
+
* @returns Manifest highlights and sizes
|
|
146
|
+
*/
|
|
147
|
+
preview(input: {
|
|
148
|
+
teamId: string;
|
|
149
|
+
stagingDir: string;
|
|
150
|
+
allowDirty?: boolean;
|
|
151
|
+
skipFlagged?: boolean;
|
|
152
|
+
}): Promise<Record<string, unknown>>;
|
|
153
|
+
/**
|
|
154
|
+
* Moves known to Cloud plus what this machine remembers of each.
|
|
155
|
+
*
|
|
156
|
+
* @returns Moves, newest first
|
|
157
|
+
*/
|
|
158
|
+
listMoves(): Promise<Array<TeamMoveView & {
|
|
159
|
+
local?: {
|
|
160
|
+
phase: string;
|
|
161
|
+
action: string | null;
|
|
162
|
+
error?: string;
|
|
163
|
+
};
|
|
164
|
+
}>>;
|
|
165
|
+
/**
|
|
166
|
+
* One move with its local progress.
|
|
167
|
+
*
|
|
168
|
+
* @param moveId - Move id
|
|
169
|
+
* @returns Cloud's view plus the local journal
|
|
170
|
+
*/
|
|
171
|
+
getMove(moveId: string): Promise<{
|
|
172
|
+
move: TeamMoveView;
|
|
173
|
+
journal: Array<{
|
|
174
|
+
step: string;
|
|
175
|
+
state: string;
|
|
176
|
+
at: string;
|
|
177
|
+
}>;
|
|
178
|
+
}>;
|
|
179
|
+
/**
|
|
180
|
+
* Owner rollback.
|
|
181
|
+
*
|
|
182
|
+
* @param moveId - Move id
|
|
183
|
+
*/
|
|
184
|
+
rollbackMove(moveId: string): Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* Housekeeping at boot and daily: purge expired archives.
|
|
187
|
+
*
|
|
188
|
+
* @returns Purged move ids
|
|
189
|
+
*/
|
|
190
|
+
housekeeping(): Promise<string[]>;
|
|
191
|
+
private readIntent;
|
|
192
|
+
/** Tell Cloud an action failed, with text the owner can act on. */
|
|
193
|
+
private reportFailure;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Turn an error into the sentence the owner sees.
|
|
197
|
+
*
|
|
198
|
+
* @param error - Thrown value
|
|
199
|
+
* @returns Message
|
|
200
|
+
*/
|
|
201
|
+
export declare function friendlyError(error: unknown): string;
|
|
202
|
+
/**
|
|
203
|
+
* Parse the data of a `team_move` push.
|
|
204
|
+
*
|
|
205
|
+
* @param payload - Relay payload
|
|
206
|
+
* @returns Parsed data, or null when malformed
|
|
207
|
+
*/
|
|
208
|
+
export declare function parseRelayData(payload: unknown): TeamMoveRelayData | null;
|
|
209
|
+
//# sourceMappingURL=team-move-driver.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team-move-driver.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/team-move/team-move-driver.service.ts"],"names":[],"mappings":"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"}
|