@tienne/gestalt 0.108.0 → 0.110.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/CLAUDE.md +1 -0
- package/dist/package.json +1 -1
- package/dist/src/cli/commands/peer.d.ts +49 -0
- package/dist/src/cli/commands/peer.d.ts.map +1 -0
- package/dist/src/cli/commands/peer.js +302 -0
- package/dist/src/cli/commands/peer.js.map +1 -0
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +81 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/peer/client.d.ts +9 -0
- package/dist/src/peer/client.d.ts.map +1 -0
- package/dist/src/peer/client.js +51 -0
- package/dist/src/peer/client.js.map +1 -0
- package/dist/src/peer/daemon.d.ts +83 -0
- package/dist/src/peer/daemon.d.ts.map +1 -0
- package/dist/src/peer/daemon.js +516 -0
- package/dist/src/peer/daemon.js.map +1 -0
- package/dist/src/peer/errors.d.ts +2 -0
- package/dist/src/peer/errors.d.ts.map +1 -0
- package/dist/src/peer/errors.js +4 -0
- package/dist/src/peer/errors.js.map +1 -0
- package/dist/src/peer/feed.d.ts +103 -0
- package/dist/src/peer/feed.d.ts.map +1 -0
- package/dist/src/peer/feed.js +167 -0
- package/dist/src/peer/feed.js.map +1 -0
- package/dist/src/peer/index.d.ts +24 -0
- package/dist/src/peer/index.d.ts.map +1 -0
- package/dist/src/peer/index.js +14 -0
- package/dist/src/peer/index.js.map +1 -0
- package/dist/src/peer/keys.d.ts +26 -0
- package/dist/src/peer/keys.d.ts.map +1 -0
- package/dist/src/peer/keys.js +95 -0
- package/dist/src/peer/keys.js.map +1 -0
- package/dist/src/peer/locate.d.ts +44 -0
- package/dist/src/peer/locate.d.ts.map +1 -0
- package/dist/src/peer/locate.js +116 -0
- package/dist/src/peer/locate.js.map +1 -0
- package/dist/src/peer/lock.d.ts +32 -0
- package/dist/src/peer/lock.d.ts.map +1 -0
- package/dist/src/peer/lock.js +147 -0
- package/dist/src/peer/lock.js.map +1 -0
- package/dist/src/peer/mirror.d.ts +21 -0
- package/dist/src/peer/mirror.d.ts.map +1 -0
- package/dist/src/peer/mirror.js +68 -0
- package/dist/src/peer/mirror.js.map +1 -0
- package/dist/src/peer/network.d.ts +148 -0
- package/dist/src/peer/network.d.ts.map +1 -0
- package/dist/src/peer/network.js +809 -0
- package/dist/src/peer/network.js.map +1 -0
- package/dist/src/peer/paths.d.ts +23 -0
- package/dist/src/peer/paths.d.ts.map +1 -0
- package/dist/src/peer/paths.js +50 -0
- package/dist/src/peer/paths.js.map +1 -0
- package/dist/src/peer/protocol.d.ts +112 -0
- package/dist/src/peer/protocol.d.ts.map +1 -0
- package/dist/src/peer/protocol.js +8 -0
- package/dist/src/peer/protocol.js.map +1 -0
- package/dist/src/peer/redact.d.ts +30 -0
- package/dist/src/peer/redact.d.ts.map +1 -0
- package/dist/src/peer/redact.js +214 -0
- package/dist/src/peer/redact.js.map +1 -0
- package/dist/src/peer/relay.d.ts +70 -0
- package/dist/src/peer/relay.d.ts.map +1 -0
- package/dist/src/peer/relay.js +410 -0
- package/dist/src/peer/relay.js.map +1 -0
- package/dist/src/peer/roster.d.ts +89 -0
- package/dist/src/peer/roster.d.ts.map +1 -0
- package/dist/src/peer/roster.js +160 -0
- package/dist/src/peer/roster.js.map +1 -0
- package/dist/src/peer/seal.d.ts +37 -0
- package/dist/src/peer/seal.d.ts.map +1 -0
- package/dist/src/peer/seal.js +115 -0
- package/dist/src/peer/seal.js.map +1 -0
- package/dist/src/peer/share.d.ts +41 -0
- package/dist/src/peer/share.d.ts.map +1 -0
- package/dist/src/peer/share.js +70 -0
- package/dist/src/peer/share.js.map +1 -0
- package/dist/src/peer/state.d.ts +68 -0
- package/dist/src/peer/state.d.ts.map +1 -0
- package/dist/src/peer/state.js +161 -0
- package/dist/src/peer/state.js.map +1 -0
- package/dist/src/peer/tail.d.ts +55 -0
- package/dist/src/peer/tail.d.ts.map +1 -0
- package/dist/src/peer/tail.js +148 -0
- package/dist/src/peer/tail.js.map +1 -0
- package/dist/src/peer/transform.d.ts +79 -0
- package/dist/src/peer/transform.d.ts.map +1 -0
- package/dist/src/peer/transform.js +237 -0
- package/dist/src/peer/transform.js.map +1 -0
- package/dist/src/peer/transport.d.ts +89 -0
- package/dist/src/peer/transport.d.ts.map +1 -0
- package/dist/src/peer/transport.js +345 -0
- package/dist/src/peer/transport.js.map +1 -0
- package/dist/src/peer/viewer-html.d.ts +10 -0
- package/dist/src/peer/viewer-html.d.ts.map +1 -0
- package/dist/src/peer/viewer-html.js +165 -0
- package/dist/src/peer/viewer-html.js.map +1 -0
- package/dist/src/peer/viewer.d.ts +58 -0
- package/dist/src/peer/viewer.d.ts.map +1 -0
- package/dist/src/peer/viewer.js +249 -0
- package/dist/src/peer/viewer.js.map +1 -0
- package/dist/src/peer/wire.d.ts +90 -0
- package/dist/src/peer/wire.d.ts.map +1 -0
- package/dist/src/peer/wire.js +10 -0
- package/dist/src/peer/wire.js.map +1 -0
- package/dist/src/peer/ws.d.ts +47 -0
- package/dist/src/peer/ws.d.ts.map +1 -0
- package/dist/src/peer/ws.js +308 -0
- package/dist/src/peer/ws.js.map +1 -0
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/mcp.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { isRawKey } from './keys.js';
|
|
2
|
+
import { canonical, signText, verifyText } from './seal.js';
|
|
3
|
+
const PERSON = /^[\p{L}\p{N}._-]{1,32}$/u;
|
|
4
|
+
export function isPersonName(value) {
|
|
5
|
+
return typeof value === 'string' && PERSON.test(value);
|
|
6
|
+
}
|
|
7
|
+
export function isRelayUrl(value) {
|
|
8
|
+
if (typeof value !== 'string' || value.length > 512)
|
|
9
|
+
return false;
|
|
10
|
+
try {
|
|
11
|
+
const u = new URL(value);
|
|
12
|
+
return (u.protocol === 'ws:' || u.protocol === 'wss:') && u.host !== '';
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** 서명할 바이트. 목적 문자열을 앞에 붙여 다른 서명과 섞이지 않게 한다 */
|
|
19
|
+
function rosterSigned(roster) {
|
|
20
|
+
return `gestalt-peer-roster-v1\n${canonical(roster)}`;
|
|
21
|
+
}
|
|
22
|
+
export function signRoster(roster, key) {
|
|
23
|
+
if (roster.issuer !== key.id)
|
|
24
|
+
throw new Error('명단의 issuer와 서명하는 키가 달라요');
|
|
25
|
+
return { roster, sig: signText(key, rosterSigned(roster)) };
|
|
26
|
+
}
|
|
27
|
+
export function memberOf(roster, machineKey) {
|
|
28
|
+
return roster.members.find((m) => m.machineKey === machineKey);
|
|
29
|
+
}
|
|
30
|
+
export function isAdmin(roster, machineKey) {
|
|
31
|
+
return memberOf(roster, machineKey)?.role === 'admin';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 새 명단을 받을지 정한다.
|
|
35
|
+
*
|
|
36
|
+
* - 지금 명단이 없으면 `anchor`(팀 id나 초대한 관리자 id)가 서명하고 그 키가 새 명단의
|
|
37
|
+
* 관리자여야 한다.
|
|
38
|
+
* - 지금 명단이 있으면 판이 더 높고 지금 명단의 관리자가 서명해야 한다. 같은 판은 다시
|
|
39
|
+
* 받지 않는다 — 같은 판에 다른 내용을 서명해 돌리는 걸 막는다.
|
|
40
|
+
*/
|
|
41
|
+
export function checkRosterUpdate(current, incoming, expect) {
|
|
42
|
+
const shape = checkShape(incoming);
|
|
43
|
+
if (!shape.ok)
|
|
44
|
+
return shape;
|
|
45
|
+
const { roster, sig } = incoming;
|
|
46
|
+
if (roster.team !== expect.team)
|
|
47
|
+
return { ok: false, reason: '다른 팀의 명단이에요' };
|
|
48
|
+
if (!verifyText(roster.issuer, rosterSigned(roster), sig)) {
|
|
49
|
+
return { ok: false, reason: '명단 서명이 맞지 않아요' };
|
|
50
|
+
}
|
|
51
|
+
if (!isAdmin(roster, roster.issuer)) {
|
|
52
|
+
return { ok: false, reason: '서명한 키가 새 명단의 관리자가 아니에요' };
|
|
53
|
+
}
|
|
54
|
+
if (!current) {
|
|
55
|
+
if (roster.issuer !== expect.anchor) {
|
|
56
|
+
return { ok: false, reason: '믿을 수 있는 관리자가 서명한 명단이 아니에요' };
|
|
57
|
+
}
|
|
58
|
+
return { ok: true };
|
|
59
|
+
}
|
|
60
|
+
if (roster.epoch <= current.roster.epoch) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
reason: `판이 낮거나 같아요 (지금 ${current.roster.epoch}, 받은 것 ${roster.epoch})`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (!isAdmin(current.roster, roster.issuer)) {
|
|
67
|
+
return { ok: false, reason: '지금 명단의 관리자가 서명하지 않았어요' };
|
|
68
|
+
}
|
|
69
|
+
return { ok: true };
|
|
70
|
+
}
|
|
71
|
+
function checkShape(value) {
|
|
72
|
+
const bad = (reason) => ({ ok: false, reason: `명단 형식: ${reason}` });
|
|
73
|
+
if (typeof value !== 'object' || value === null)
|
|
74
|
+
return bad('객체가 아니에요');
|
|
75
|
+
const { roster, sig } = value;
|
|
76
|
+
if (typeof sig !== 'string')
|
|
77
|
+
return bad('서명이 없어요');
|
|
78
|
+
if (typeof roster !== 'object' || roster === null)
|
|
79
|
+
return bad('명단이 없어요');
|
|
80
|
+
if (roster.v !== 1)
|
|
81
|
+
return bad('버전');
|
|
82
|
+
if (!isRawKey(roster.team) || !isRawKey(roster.issuer))
|
|
83
|
+
return bad('키');
|
|
84
|
+
if (!Number.isSafeInteger(roster.epoch) || roster.epoch < 1)
|
|
85
|
+
return bad('판');
|
|
86
|
+
if (!Number.isSafeInteger(roster.issuedAt))
|
|
87
|
+
return bad('시각');
|
|
88
|
+
if (!isRelayUrl(roster.relay))
|
|
89
|
+
return bad('릴레이 주소');
|
|
90
|
+
if (!Array.isArray(roster.members) || roster.members.length === 0)
|
|
91
|
+
return bad('멤버');
|
|
92
|
+
if (roster.members.length > 1000)
|
|
93
|
+
return bad('멤버가 너무 많아요');
|
|
94
|
+
const seen = new Set();
|
|
95
|
+
for (const m of roster.members) {
|
|
96
|
+
if (typeof m !== 'object' || m === null)
|
|
97
|
+
return bad('멤버');
|
|
98
|
+
if (!isPersonName(m.person))
|
|
99
|
+
return bad('이름');
|
|
100
|
+
if (!isRawKey(m.machineKey) || !isRawKey(m.boxKey))
|
|
101
|
+
return bad('멤버 키');
|
|
102
|
+
if (m.role !== 'admin' && m.role !== 'member')
|
|
103
|
+
return bad('역할');
|
|
104
|
+
if (!Number.isSafeInteger(m.addedAt))
|
|
105
|
+
return bad('추가 시각');
|
|
106
|
+
if (seen.has(m.machineKey))
|
|
107
|
+
return bad('같은 기기가 두 번 있어요');
|
|
108
|
+
seen.add(m.machineKey);
|
|
109
|
+
}
|
|
110
|
+
return { ok: true };
|
|
111
|
+
}
|
|
112
|
+
/** 같은 내용을 다시 서명하지 않고 한 판 올린 새 명단을 만든다 */
|
|
113
|
+
export function nextRoster(current, members, issuer, now = Date.now()) {
|
|
114
|
+
return { ...current, epoch: current.epoch + 1, members, issuedAt: now, issuer };
|
|
115
|
+
}
|
|
116
|
+
const INVITE_PREFIX = 'gpi1.';
|
|
117
|
+
export function encodeInvite(invite) {
|
|
118
|
+
return INVITE_PREFIX + Buffer.from(JSON.stringify(invite), 'utf8').toString('base64url');
|
|
119
|
+
}
|
|
120
|
+
export function decodeInvite(code) {
|
|
121
|
+
const trimmed = code.trim();
|
|
122
|
+
if (!trimmed.startsWith(INVITE_PREFIX))
|
|
123
|
+
throw new Error('초대 코드 꼴이 아니에요');
|
|
124
|
+
let v;
|
|
125
|
+
try {
|
|
126
|
+
v = JSON.parse(Buffer.from(trimmed.slice(INVITE_PREFIX.length), 'base64url').toString('utf8'));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
throw new Error('초대 코드를 못 읽었어요');
|
|
130
|
+
}
|
|
131
|
+
if (v.v !== 1 ||
|
|
132
|
+
!isRawKey(v.team) ||
|
|
133
|
+
!isRawKey(v.admin) ||
|
|
134
|
+
!isRawKey(v.adminBox) ||
|
|
135
|
+
!isRelayUrl(v.relay) ||
|
|
136
|
+
typeof v.id !== 'string' ||
|
|
137
|
+
!/^[A-Za-z0-9_-]{8,64}$/.test(v.id) ||
|
|
138
|
+
typeof v.secret !== 'string' ||
|
|
139
|
+
!/^[A-Za-z0-9_-]{43}$/.test(v.secret) ||
|
|
140
|
+
!isPersonName(v.person) ||
|
|
141
|
+
!Number.isSafeInteger(v.expiresAt)) {
|
|
142
|
+
throw new Error('초대 코드 내용이 맞지 않아요');
|
|
143
|
+
}
|
|
144
|
+
return v;
|
|
145
|
+
}
|
|
146
|
+
/** join 요청에 싣는 증명. 초대 코드를 가진 사람만 만든다 */
|
|
147
|
+
export function joinProofText(inviteId, machineKey, boxKey) {
|
|
148
|
+
return canonical({ p: 'gestalt-peer-join-v1', invite: inviteId, key: machineKey, box: boxKey });
|
|
149
|
+
}
|
|
150
|
+
const LINK = /^gestalt:\/\/peer\/([^/#?\s]{1,255})\/([A-Za-z0-9_-]{1,64})#([A-Za-z0-9_-]{43})$/;
|
|
151
|
+
export function formatShareLink(relayUrl, shareId, shareKey) {
|
|
152
|
+
return `gestalt://peer/${new URL(relayUrl).host}/${shareId}#${shareKey}`;
|
|
153
|
+
}
|
|
154
|
+
export function parseShareLink(link) {
|
|
155
|
+
const m = LINK.exec(link.trim());
|
|
156
|
+
if (!m)
|
|
157
|
+
throw new Error('공유 링크 꼴이 아니에요 (gestalt://peer/<릴레이>/<공유 id>#<키>)');
|
|
158
|
+
return { relayHost: m[1], shareId: m[2], shareKey: m[3] };
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=roster.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roster.js","sourceRoot":"","sources":["../../../src/peer/roster.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { MachineKey } from './keys.js';
|
|
2
|
+
/**
|
|
3
|
+
* 봉인과 서명. 전부 `node:crypto`다.
|
|
4
|
+
*
|
|
5
|
+
* - box: 받는 기기의 X25519 공개키로 봉인하고 보낸 기기의 Ed25519 키로 서명한다.
|
|
6
|
+
* 보낼 때마다 임시 X25519 키를 만들어서 같은 두 기기 사이에도 키가 매번 다르다.
|
|
7
|
+
* - aead: 공유 세션 키 하나로 여는 AES-256-GCM. 공유 스트림 프레임에 쓴다.
|
|
8
|
+
*/
|
|
9
|
+
/** 키를 정렬한 JSON. 서명할 바이트를 두 쪽이 똑같이 만들려고 쓴다 */
|
|
10
|
+
export declare function canonical(value: unknown): string;
|
|
11
|
+
export declare function signText(key: MachineKey, text: string): string;
|
|
12
|
+
/** 서명이 맞는지. 키나 서명 꼴이 틀려도 던지지 않고 false */
|
|
13
|
+
export declare function verifyText(signer: string, text: string, sig: unknown): boolean;
|
|
14
|
+
export declare function deriveKey(ikm: Buffer, salt: Buffer, info: string): Buffer;
|
|
15
|
+
export declare function hmac(key: Buffer, text: string): string;
|
|
16
|
+
export declare function safeEqual(a: unknown, b: string): boolean;
|
|
17
|
+
/** nonce 12바이트 + 암호문 + 태그 16바이트를 base64url 한 줄로 */
|
|
18
|
+
export declare function aeadSeal(key: Buffer, plaintext: Buffer, aad: string): string;
|
|
19
|
+
/** 열지 못하면 null. 키가 틀렸거나 누가 바꿨거나 aad가 다르다 */
|
|
20
|
+
export declare function aeadOpen(key: Buffer, sealed: unknown, aad: string): Buffer | null;
|
|
21
|
+
export interface Box {
|
|
22
|
+
k: 'box';
|
|
23
|
+
/** 임시 X25519 공개키 */
|
|
24
|
+
e: string;
|
|
25
|
+
/** aeadSeal 결과 */
|
|
26
|
+
c: string;
|
|
27
|
+
/** 보낸 기기의 서명 */
|
|
28
|
+
s: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `to`(받는 기기 id)와 `toBox`(그 기기의 X25519 공개키)에게 봉인한다. 서명에 받는 기기
|
|
32
|
+
* id를 넣어서 다른 기기에게 간 봉인을 그대로 옮겨 보내도 서명이 안 맞는다.
|
|
33
|
+
*/
|
|
34
|
+
export declare function sealBox(from: MachineKey, to: string, toBox: string, plaintext: string): Box;
|
|
35
|
+
/** 보낸 기기 서명이 맞고 열리면 평문. 아니면 null */
|
|
36
|
+
export declare function openBox(me: MachineKey, from: string, box: unknown): string | null;
|
|
37
|
+
//# sourceMappingURL=seal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal.d.ts","sourceRoot":"","sources":["../../../src/peer/seal.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C;;;;;;GAMG;AAEH,6CAA6C;AAC7C,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQhD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAY9E;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAKxD;AAED,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAM5E;AAED,4CAA4C;AAC5C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYjF;AAED,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,KAAK,CAAC;IACT,oBAAoB;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,kBAAkB;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,gBAAgB;IAChB,CAAC,EAAE,MAAM,CAAC;CACX;AAID;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,GAAG,CAM3F;AAED,oCAAoC;AACpC,wBAAgB,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAWjF"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHmac, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, sign, timingSafeEqual, verify, } from 'node:crypto';
|
|
2
|
+
import { boxPublicKey, isRawKey, signPublicKey } from './keys.js';
|
|
3
|
+
/**
|
|
4
|
+
* 봉인과 서명. 전부 `node:crypto`다.
|
|
5
|
+
*
|
|
6
|
+
* - box: 받는 기기의 X25519 공개키로 봉인하고 보낸 기기의 Ed25519 키로 서명한다.
|
|
7
|
+
* 보낼 때마다 임시 X25519 키를 만들어서 같은 두 기기 사이에도 키가 매번 다르다.
|
|
8
|
+
* - aead: 공유 세션 키 하나로 여는 AES-256-GCM. 공유 스트림 프레임에 쓴다.
|
|
9
|
+
*/
|
|
10
|
+
/** 키를 정렬한 JSON. 서명할 바이트를 두 쪽이 똑같이 만들려고 쓴다 */
|
|
11
|
+
export function canonical(value) {
|
|
12
|
+
if (value === null || typeof value !== 'object')
|
|
13
|
+
return JSON.stringify(value);
|
|
14
|
+
if (Array.isArray(value))
|
|
15
|
+
return `[${value.map(canonical).join(',')}]`;
|
|
16
|
+
const obj = value;
|
|
17
|
+
const keys = Object.keys(obj)
|
|
18
|
+
.filter((k) => obj[k] !== undefined)
|
|
19
|
+
.sort();
|
|
20
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(',')}}`;
|
|
21
|
+
}
|
|
22
|
+
export function signText(key, text) {
|
|
23
|
+
return sign(null, Buffer.from(text, 'utf8'), key.signPrivate).toString('base64url');
|
|
24
|
+
}
|
|
25
|
+
/** 서명이 맞는지. 키나 서명 꼴이 틀려도 던지지 않고 false */
|
|
26
|
+
export function verifyText(signer, text, sig) {
|
|
27
|
+
if (!isRawKey(signer) || typeof sig !== 'string')
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
return verify(null, Buffer.from(text, 'utf8'), signPublicKey(signer), Buffer.from(sig, 'base64url'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function deriveKey(ikm, salt, info) {
|
|
37
|
+
return Buffer.from(hkdfSync('sha256', ikm, salt, Buffer.from(info, 'utf8'), 32));
|
|
38
|
+
}
|
|
39
|
+
export function hmac(key, text) {
|
|
40
|
+
return createHmac('sha256', key).update(text, 'utf8').digest('base64url');
|
|
41
|
+
}
|
|
42
|
+
export function safeEqual(a, b) {
|
|
43
|
+
if (typeof a !== 'string')
|
|
44
|
+
return false;
|
|
45
|
+
const x = Buffer.from(a);
|
|
46
|
+
const y = Buffer.from(b);
|
|
47
|
+
return x.length === y.length && timingSafeEqual(x, y);
|
|
48
|
+
}
|
|
49
|
+
/** nonce 12바이트 + 암호문 + 태그 16바이트를 base64url 한 줄로 */
|
|
50
|
+
export function aeadSeal(key, plaintext, aad) {
|
|
51
|
+
const nonce = randomBytes(12);
|
|
52
|
+
const cipher = createCipheriv('aes-256-gcm', key, nonce);
|
|
53
|
+
cipher.setAAD(Buffer.from(aad, 'utf8'));
|
|
54
|
+
const body = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
55
|
+
return Buffer.concat([nonce, body, cipher.getAuthTag()]).toString('base64url');
|
|
56
|
+
}
|
|
57
|
+
/** 열지 못하면 null. 키가 틀렸거나 누가 바꿨거나 aad가 다르다 */
|
|
58
|
+
export function aeadOpen(key, sealed, aad) {
|
|
59
|
+
if (typeof sealed !== 'string')
|
|
60
|
+
return null;
|
|
61
|
+
const raw = Buffer.from(sealed, 'base64url');
|
|
62
|
+
if (raw.length < 28)
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
const decipher = createDecipheriv('aes-256-gcm', key, raw.subarray(0, 12));
|
|
66
|
+
decipher.setAAD(Buffer.from(aad, 'utf8'));
|
|
67
|
+
decipher.setAuthTag(raw.subarray(raw.length - 16));
|
|
68
|
+
return Buffer.concat([decipher.update(raw.subarray(12, raw.length - 16)), decipher.final()]);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const BOX_INFO = 'gestalt-peer-box-v1';
|
|
75
|
+
/**
|
|
76
|
+
* `to`(받는 기기 id)와 `toBox`(그 기기의 X25519 공개키)에게 봉인한다. 서명에 받는 기기
|
|
77
|
+
* id를 넣어서 다른 기기에게 간 봉인을 그대로 옮겨 보내도 서명이 안 맞는다.
|
|
78
|
+
*/
|
|
79
|
+
export function sealBox(from, to, toBox, plaintext) {
|
|
80
|
+
const eph = generateKeyPairSync('x25519');
|
|
81
|
+
const e = eph.publicKey.export({ format: 'jwk' }).x;
|
|
82
|
+
const key = boxKey(eph.privateKey, boxPublicKey(toBox), e, toBox);
|
|
83
|
+
const c = aeadSeal(key, Buffer.from(plaintext, 'utf8'), `${BOX_INFO}|${from.id}|${to}`);
|
|
84
|
+
return { k: 'box', e, c, s: signText(from, boxSigned(from.id, to, e, c)) };
|
|
85
|
+
}
|
|
86
|
+
/** 보낸 기기 서명이 맞고 열리면 평문. 아니면 null */
|
|
87
|
+
export function openBox(me, from, box) {
|
|
88
|
+
if (!isBox(box))
|
|
89
|
+
return null;
|
|
90
|
+
if (!verifyText(from, boxSigned(from, me.id, box.e, box.c), box.s))
|
|
91
|
+
return null;
|
|
92
|
+
let key;
|
|
93
|
+
try {
|
|
94
|
+
key = boxKey(me.boxPrivate, boxPublicKey(box.e), box.e, me.boxKey);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const plain = aeadOpen(key, box.c, `${BOX_INFO}|${from}|${me.id}`);
|
|
100
|
+
return plain ? plain.toString('utf8') : null;
|
|
101
|
+
}
|
|
102
|
+
function boxSigned(from, to, e, c) {
|
|
103
|
+
return `${BOX_INFO}\n${from}\n${to}\n${e}\n${c}`;
|
|
104
|
+
}
|
|
105
|
+
function boxKey(priv, pub, ephRaw, recipientRaw) {
|
|
106
|
+
const shared = diffieHellman({ privateKey: priv, publicKey: pub });
|
|
107
|
+
return deriveKey(shared, Buffer.from(ephRaw + recipientRaw, 'utf8'), BOX_INFO);
|
|
108
|
+
}
|
|
109
|
+
function isBox(v) {
|
|
110
|
+
if (typeof v !== 'object' || v === null)
|
|
111
|
+
return false;
|
|
112
|
+
const b = v;
|
|
113
|
+
return b.k === 'box' && isRawKey(b.e) && typeof b.c === 'string' && typeof b.s === 'string';
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=seal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal.js","sourceRoot":"","sources":["../../../src/peer/seal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { TailOptions } from './tail.js';
|
|
2
|
+
import type { RedactOptions, ToolsLevel } from './redact.js';
|
|
3
|
+
import { ShareFeed } from './feed.js';
|
|
4
|
+
export { DEFAULT_MAX_BACKLOG_BYTES } from './feed.js';
|
|
5
|
+
export type { BacklogSlice, ShareFrame, ShareListener, ShareStatus } from './feed.js';
|
|
6
|
+
export interface ShareStreamOptions extends RedactOptions {
|
|
7
|
+
id: string;
|
|
8
|
+
transcriptPath: string;
|
|
9
|
+
tools: ToolsLevel;
|
|
10
|
+
/** 들고 있는 이벤트의 직렬화 바이트 상한. 테스트가 작게 준다 */
|
|
11
|
+
maxBacklogBytes?: number;
|
|
12
|
+
tail?: Omit<TailOptions, 'startOffset'>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 공유 하나. 대화 기록을 따라 읽어 바꾼 이벤트를 메모리에 쌓고 붙은 뷰어에 흘린다.
|
|
16
|
+
*
|
|
17
|
+
* 바꾼 결과만 메모리에 들고 원문 줄은 들지 않는다. 디스크에는 아무것도 안 쓴다.
|
|
18
|
+
* 처음 붙는 뷰어가 앞부분을 받아야 해서 바꾼 이벤트를 직렬화한 글자로 상한까지 남긴다.
|
|
19
|
+
*/
|
|
20
|
+
export declare class ShareStream extends ShareFeed {
|
|
21
|
+
readonly transcriptPath: string;
|
|
22
|
+
readonly tools: ToolsLevel;
|
|
23
|
+
private tail;
|
|
24
|
+
private transformer;
|
|
25
|
+
private title;
|
|
26
|
+
private lastUnknown;
|
|
27
|
+
constructor(options: ShareStreamOptions);
|
|
28
|
+
get unknownLines(): number;
|
|
29
|
+
get failedLines(): number;
|
|
30
|
+
get oversizedLines(): number;
|
|
31
|
+
get claudeVersion(): string | undefined;
|
|
32
|
+
get sessionTitle(): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* 새로 쓰인 줄을 읽어 흘린다. 데몬이 주기적으로 부른다.
|
|
35
|
+
* 모르는 줄 수가 늘었으면 true — 데몬이 감사 로그에 버전을 남길 때 쓴다.
|
|
36
|
+
*/
|
|
37
|
+
poll(): {
|
|
38
|
+
unknownGrew: boolean;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=share.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../../../src/peer/share.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,WAAW,CAAC;AAEzD,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAEtF,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,wCAAwC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;CACzC;AAED;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,SAAS;IACxC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAE3B,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,WAAW,CAAK;gBAEZ,OAAO,EAAE,kBAAkB;IAQvC,IAAa,YAAY,IAAI,MAAM,CAElC;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAEtC;IAED,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED;;;OAGG;IACH,IAAI,IAAI;QAAE,WAAW,EAAE,OAAO,CAAA;KAAE;CAwBjC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { TranscriptTail } from './tail.js';
|
|
2
|
+
import { TranscriptTransformer } from './transform.js';
|
|
3
|
+
import { ShareFeed } from './feed.js';
|
|
4
|
+
export { DEFAULT_MAX_BACKLOG_BYTES } from './feed.js';
|
|
5
|
+
/**
|
|
6
|
+
* 공유 하나. 대화 기록을 따라 읽어 바꾼 이벤트를 메모리에 쌓고 붙은 뷰어에 흘린다.
|
|
7
|
+
*
|
|
8
|
+
* 바꾼 결과만 메모리에 들고 원문 줄은 들지 않는다. 디스크에는 아무것도 안 쓴다.
|
|
9
|
+
* 처음 붙는 뷰어가 앞부분을 받아야 해서 바꾼 이벤트를 직렬화한 글자로 상한까지 남긴다.
|
|
10
|
+
*/
|
|
11
|
+
export class ShareStream extends ShareFeed {
|
|
12
|
+
transcriptPath;
|
|
13
|
+
tools;
|
|
14
|
+
tail;
|
|
15
|
+
transformer;
|
|
16
|
+
title;
|
|
17
|
+
lastUnknown = 0;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
super(options.id, options.maxBacklogBytes);
|
|
20
|
+
this.transcriptPath = options.transcriptPath;
|
|
21
|
+
this.tools = options.tools;
|
|
22
|
+
this.tail = new TranscriptTail(options.transcriptPath, options.tail);
|
|
23
|
+
this.transformer = new TranscriptTransformer({ tools: options.tools, home: options.home });
|
|
24
|
+
}
|
|
25
|
+
get unknownLines() {
|
|
26
|
+
return this.transformer.unknownLines;
|
|
27
|
+
}
|
|
28
|
+
get failedLines() {
|
|
29
|
+
return this.transformer.failedLines;
|
|
30
|
+
}
|
|
31
|
+
get oversizedLines() {
|
|
32
|
+
return this.tail.oversizedLines;
|
|
33
|
+
}
|
|
34
|
+
get claudeVersion() {
|
|
35
|
+
return this.transformer.version;
|
|
36
|
+
}
|
|
37
|
+
get sessionTitle() {
|
|
38
|
+
return this.title;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 새로 쓰인 줄을 읽어 흘린다. 데몬이 주기적으로 부른다.
|
|
42
|
+
* 모르는 줄 수가 늘었으면 true — 데몬이 감사 로그에 버전을 남길 때 쓴다.
|
|
43
|
+
*/
|
|
44
|
+
poll() {
|
|
45
|
+
if (this.isClosed || this.status === 'truncated') {
|
|
46
|
+
return { unknownGrew: false };
|
|
47
|
+
}
|
|
48
|
+
const { lines, status } = this.tail.read();
|
|
49
|
+
this.setStatus(toShareStatus(status));
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
for (const event of this.transformer.push(line)) {
|
|
52
|
+
if (event.kind === 'title')
|
|
53
|
+
this.title = event.text;
|
|
54
|
+
this.pushEvent(JSON.stringify(event));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
this.trim();
|
|
58
|
+
const unknown = this.transformer.unknownLines;
|
|
59
|
+
const unknownGrew = unknown > this.lastUnknown;
|
|
60
|
+
if (unknownGrew) {
|
|
61
|
+
this.lastUnknown = unknown;
|
|
62
|
+
this.emit({ type: 'stats', unknownLines: unknown });
|
|
63
|
+
}
|
|
64
|
+
return { unknownGrew };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function toShareStatus(status) {
|
|
68
|
+
return status === 'ok' ? 'live' : status;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=share.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share.js","sourceRoot":"","sources":["../../../src/peer/share.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ToolsLevel } from './redact.js';
|
|
2
|
+
import type { SignedRoster } from './roster.js';
|
|
3
|
+
export type ShareCloseReason = 'unshare' | 'expired' | 'daemon_stop' | 'daemon_restart' | 'error';
|
|
4
|
+
export interface ShareRow {
|
|
5
|
+
id: string;
|
|
6
|
+
sessionId: string | null;
|
|
7
|
+
transcriptPath: string;
|
|
8
|
+
tools: ToolsLevel;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
closedAt: number | null;
|
|
12
|
+
closeReason: string | null;
|
|
13
|
+
}
|
|
14
|
+
export type AuditAction = 'share_open' | 'share_close' | 'viewer_connect' | 'viewer_disconnect' | 'unknown_lines' | 'team_init' | 'team_join' | 'invite_create' | 'member_join' | 'member_remove' | 'roster_accept' | 'roster_reject' | 'remote_watch' | 'remote_watch_end' | 'watch_open' | 'watch_close';
|
|
15
|
+
/** 이 기기가 든 팀. 한 기기는 팀 하나에만 든다 */
|
|
16
|
+
export interface TeamConfig {
|
|
17
|
+
team: string;
|
|
18
|
+
relay: string;
|
|
19
|
+
person: string;
|
|
20
|
+
/** 명단을 하나도 못 들고 있을 때 믿을 관리자 키. 팀을 만든 기기면 자기 자신이다 */
|
|
21
|
+
anchor: string;
|
|
22
|
+
}
|
|
23
|
+
export interface InviteRow {
|
|
24
|
+
id: string;
|
|
25
|
+
secret: string;
|
|
26
|
+
person: string;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
expiresAt: number;
|
|
29
|
+
usedAt: number | null;
|
|
30
|
+
usedBy: string | null;
|
|
31
|
+
}
|
|
32
|
+
export interface AuditRow {
|
|
33
|
+
at: number;
|
|
34
|
+
shareId: string | null;
|
|
35
|
+
action: AuditAction;
|
|
36
|
+
detail: string | null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 공유 목록, 팀 설정과 명단, 초대, 감사 로그. 대화 내용은 한 글자도 여기 안 들어온다 —
|
|
40
|
+
* 줄 수나 버전 같은 숫자와 이름만 남긴다. 공유 토큰과 공유 키도 저장하지 않는다. 데몬이
|
|
41
|
+
* 다시 뜨면 이전 공유는 종료된 걸로 치고 새로 연다.
|
|
42
|
+
*
|
|
43
|
+
* 초대 비밀은 저장한다. 관리자 데몬이 나중에 온 join 요청의 증명을 맞춰 봐야 해서다.
|
|
44
|
+
* 파일은 0600이고 쓰고 나면 used_at이 찬다.
|
|
45
|
+
*/
|
|
46
|
+
export declare class PeerState {
|
|
47
|
+
private db;
|
|
48
|
+
constructor(path: string);
|
|
49
|
+
insertShare(row: Omit<ShareRow, 'closedAt' | 'closeReason'>): void;
|
|
50
|
+
closeShare(id: string, reason: ShareCloseReason, at?: number): void;
|
|
51
|
+
/** 데몬이 죽어서 종료 기록 없이 남은 공유를 종료한다. 종료한 id 목록을 돌려준다 */
|
|
52
|
+
closeDangling(reason: ShareCloseReason, at?: number): string[];
|
|
53
|
+
getShare(id: string): ShareRow | undefined;
|
|
54
|
+
getTeam(): TeamConfig | null;
|
|
55
|
+
setTeam(team: TeamConfig): void;
|
|
56
|
+
/** 받은 명단 중 가장 새것. 받을지는 부르는 쪽이 checkRosterUpdate로 정한 뒤다 */
|
|
57
|
+
getRoster(): SignedRoster | null;
|
|
58
|
+
setRoster(roster: SignedRoster): void;
|
|
59
|
+
insertInvite(row: Omit<InviteRow, 'usedAt' | 'usedBy'>): void;
|
|
60
|
+
getInvite(id: string): InviteRow | undefined;
|
|
61
|
+
useInvite(id: string, machineKey: string, at?: number): void;
|
|
62
|
+
private getJson;
|
|
63
|
+
private setJson;
|
|
64
|
+
audit(action: AuditAction, shareId: string | null, detail?: string, at?: number): void;
|
|
65
|
+
auditLog(shareId?: string): AuditRow[];
|
|
66
|
+
close(): void;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/peer/state.ts"],"names":[],"mappings":"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"}
|