1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 | /**
* TS smoke-compile of the exported API — exercises exactly the constructs the
* nine suite apps use (`new IdBridge`, static async factories, getters,
* `import type`, inline `import("pkg").Type`, subpath imports), plus the rest
* of the surface. Compiled by `npm run check`; never executed. This is the
* tripwire for both `.d.ts` drift and advanced-rename breakage.
*/
import {
// the apps' import lists (chat/board/games/home)
IdBridge,
Identity,
verifyAssertion,
verifyRaw,
fingerprintOf,
clampProfile,
hueOfPub,
effectiveHue,
colorOfPub,
defaultGlyph,
initBridgeHost,
readBridgeRecord,
writeBridgeRecord,
sanitizeRecord,
isEd25519Supported,
isAllowedAppOrigin,
appKeyOfOrigin,
isBridgeReply,
isBridgeRequest,
EMPTY_PROFILE,
MAX_NAME_LEN,
MAX_RECORD_BYTES,
MAX_APP_STATE_BYTES,
RESERVED_CORE_BYTES,
MAX_APPS_BYTES,
BRIDGE_PATH,
DEFAULT_BRIDGE_URL,
BRIDGE_STORE_KEY,
SEED_RE,
APP_ORIGIN_RE,
APP_KEY_RE,
RESERVED_APP_KEYS,
DEV_ORIGINS,
// crypto primitives
utf8,
randomBytes,
toB64,
fromB64,
toB64url,
fromB64url,
} from "ardegazu-id-kit";
import type {
Bytes,
Fingerprint,
Profile,
ProfilePatch,
IdRecord,
AppEntry,
AppStateResult,
AppWriteRefusal,
BridgeRequest,
BridgeReply,
IdBridgeOptions,
BootResult,
} from "ardegazu-id-kit";
import {
SUITE_SALT,
deriveXKeyPair,
deriveSuiteXKeyPair,
signXCert,
verifyXCert,
wrapTo,
unwrap,
} from "ardegazu-id-kit/xkey";
import type { XKeyPair, Wrap } from "ardegazu-id-kit/xkey";
import { runIdKitSelfTest } from "ardegazu-id-kit/selftest";
// inline import() types (the way app code annotates without a value import)
type InlineProfile = import("ardegazu-id-kit").Profile;
type InlineWrap = import("ardegazu-id-kit/xkey").Wrap;
export async function smoke(): Promise<void> {
// ---- bridge client: the boot every app runs ------------------------------
const opts: IdBridgeOptions = { ns: "chat-ardegazu-ro-v2", bridgeUrl: "https://ardegazu.ro/id/", timeoutMs: 2500 };
const bridge = new IdBridge(opts);
const boot: BootResult = await bridge.boot();
const seed: string | null = boot.seed;
const bootId: Identity | null = boot.identity;
const source: "mirror" | "bridge" | "fresh" | "none" = boot.source;
const conflict: IdRecord | null = boot.conflict;
const bridged: boolean = boot.bridged;
const mirrorSeed: string | null = bridge.mirrorSeed();
const mp: Profile & { ts: number } = bridge.mirrorProfile();
const off: () => void = bridge.onChange((rec: IdRecord | null) => void rec);
const last: IdRecord | null = bridge.lastRecord();
const patch: ProfilePatch = { name: "Ana", hue: 200 };
await bridge.putProfile(patch);
const socOk: boolean = await bridge.putSoc({ friends: [] });
// ---- app sections: write our own, read everyone's ------------------------
const appOk: boolean = await bridge.putAppState({ banks: ["ing"] });
const detailed: AppStateResult = await bridge.putAppStateDetailed({ banks: ["ing"] });
const why: AppStateResult["reason"] = detailed.reason;
const refusal: AppWriteRefusal = "size";
const cleared: boolean = await bridge.putAppState(null);
const myKey: string | null = bridge.appKey();
const theirs: unknown = bridge.appStateOf("banca");
const allSections: Record<string, AppEntry> | null = last?.apps ?? null;
const entryTs: number | undefined = allSections?.banca?.ts;
const keyOf: string | null = appKeyOfOrigin("https://banca.ardegazu.ro");
if (conflict) {
bridge.adoptBridgeSeed(conflict);
bridge.keepLocalSeed(conflict);
}
const published: boolean = await bridge.publishSeed(Identity.newSeed(), { force: true });
off();
bridge.destroy();
// ---- identity ------------------------------------------------------------
const id: Identity = await Identity.fromSeed(Identity.newSeed());
const pub: string = id.publicKeyB64;
const fp: Fingerprint = id.fingerprint; // getter, no call
const sig: string = await id.assert("app-salt", "room", "peer");
const raw: Bytes = await id.signRaw(utf8("payload"));
const fp2: Fingerprint | null = await verifyAssertion("app-salt", "room", "peer", pub, sig);
const rawOk: boolean = await verifyRaw(pub, toB64url(raw), utf8("payload"));
const fp3: Fingerprint = await fingerprintOf(pub);
const supported: boolean = await isEd25519Supported();
// ---- profile -------------------------------------------------------------
const prof: Profile = clampProfile({ name: "x", hue: 1, glyph: "🦊", lang: "ro" });
const hue: number = hueOfPub(pub);
const eff: number = effectiveHue(pub, prof);
const color: string = colorOfPub(pub, null);
const glyph: string = await defaultGlyph(pub);
const empty: Profile = EMPTY_PROFILE;
const maxLen: number = MAX_NAME_LEN;
// ---- protocol + host (home's /id/ page + hub direct access) --------------
initBridgeHost();
const rr: { rec: IdRecord | null; ephemeral: boolean } = readBridgeRecord();
const wrote: boolean = writeBridgeRecord(rr.rec);
const sane: IdRecord | null = sanitizeRecord({ seed: "x" });
const allowed: boolean = isAllowedAppOrigin("https://chat.ardegazu.ro");
const req: unknown = { t: "get", v: 1, reqId: 1 };
if (isBridgeRequest(req)) {
const r: BridgeRequest = req;
void r;
}
if (isBridgeReply(req)) {
const r: BridgeReply = req;
void r;
}
const seedOk: boolean = SEED_RE.test("x") && APP_ORIGIN_RE.test("https://ardegazu.ro")
&& APP_KEY_RE.test("banca");
const dev: readonly string[] = DEV_ORIGINS;
const consts: string = BRIDGE_PATH + DEFAULT_BRIDGE_URL + BRIDGE_STORE_KEY;
const cap: number = MAX_RECORD_BYTES;
const appCap: number = MAX_APP_STATE_BYTES;
const reserve: number = RESERVED_CORE_BYTES;
const appsCap: number = MAX_APPS_BYTES;
const reservedKeys: readonly string[] = RESERVED_APP_KEYS;
// ---- crypto primitives ---------------------------------------------------
const b: Bytes = randomBytes(32);
const rt: Bytes = fromB64(toB64(b));
const rt2: Bytes = fromB64url(toB64url(b));
// ---- xkey ----------------------------------------------------------------
const salt: string = SUITE_SALT;
const kp: XKeyPair = await deriveSuiteXKeyPair(Identity.newSeed());
const kp2: XKeyPair = await deriveXKeyPair(Identity.newSeed(), "someapp/v1");
const cert: string = await signXCert(id, salt, kp.pubB64);
const certOk: boolean = await verifyXCert(salt, pub, kp.pubB64, cert);
const w: Wrap = await wrapTo(salt, "ctx", pub, kp.pubB64, utf8("hello"));
const opened: Bytes | null = await unwrap(salt, "ctx", w, kp);
// ---- selftest ------------------------------------------------------------
await runIdKitSelfTest();
// keep noUnusedLocals honest
void [seed, bootId, source, bridged, mirrorSeed, mp, last, socOk, published,
appOk, detailed, why, refusal, cleared, myKey, theirs, allSections, entryTs, keyOf,
fp, fp2, rawOk, fp3, supported, hue, eff, color, glyph, empty, maxLen,
wrote, sane, allowed, seedOk, dev, consts, cap, appCap, reserve, appsCap, reservedKeys, rt, rt2, kp2, certOk, opened];
const inline: InlineProfile = prof;
const inlineW: InlineWrap = w;
void [inline, inlineW];
}
|