feat(bus): complete TypeScript SDK — auth, room envelope, client, transport
Second half of the browser-native bus SDK (issue 0001, Phase 1), making uniweb a peer of the bus in its own right (like unibus_android) without the Go gateway: - busauth.ts: NATS user nkey from the Ed25519 key (base32 + crc16, no nkeys dep) and control-plane request signing (CanonicalRequest + X-Unibus-* headers). - room.ts: Policy / Room types (ModeNATS, ModeMatrix). - client.ts: the pure room ENVELOPE (sealRoomMessage/openRoomMessage — AEAD with the subject as AAD, Ed25519 sign, drop on verify/decrypt failure), a transport- agnostic BusClient, and a signed ControlPlane HTTP client (fetch room/key/members, open the sealed room key locally). - wstransport.ts: concrete nats.ws WebSocket transport (validated E2E in Phase 3). - index.ts: public SDK surface. Parity pinned by vectors from unibus cmd/busvectors (extended with nkey + signed control-request vectors): 19/19 green. The user's private key signs everything in the browser and is never sent to any server. Bumps uniweb to 0.2.0. Remaining for Phase 1 completion: the live nats.ws connection + control-plane, which need a running unibus with the WebSocket listener — exercised in Phase 3.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
// Parity tests for the auth bridge: the browser must produce the same NATS nkey and
|
||||
// the same signed control-plane request bytes as the Go client, or it would not
|
||||
// authenticate on either plane (issue 0001, Phase 1).
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import vectors from "./testdata/vectors.json";
|
||||
import { hexToBytes, bytesToHex, base64ToBytes } from "./crypto.js";
|
||||
import { nkeyPublic, canonicalRequest, signedHeaders } from "./busauth.js";
|
||||
|
||||
describe("NATS nkey encoding", () => {
|
||||
it("derives the same user nkey ('U...') as Go from the Ed25519 pubkey", () => {
|
||||
const v = vectors.nkey;
|
||||
expect(nkeyPublic(hexToBytes(v.sign_pub_hex))).toBe(v.nkey_public);
|
||||
});
|
||||
});
|
||||
|
||||
describe("control-plane request signing", () => {
|
||||
it("builds the same canonical request bytes as Go", () => {
|
||||
const v = vectors.control_request;
|
||||
const got = canonicalRequest(v.method, v.path, v.ts, v.nonce, hexToBytes(v.body_hex));
|
||||
expect(bytesToHex(got)).toBe(v.canonical_hex);
|
||||
});
|
||||
|
||||
it("produces the same Ed25519 signature as Go (X-Unibus-Sig)", () => {
|
||||
const v = vectors.control_request;
|
||||
const headers = signedHeaders(
|
||||
hexToBytes(vectors.sign.sign_pub_hex),
|
||||
hexToBytes(v.sign_priv_hex),
|
||||
v.method,
|
||||
v.path,
|
||||
v.ts,
|
||||
v.nonce,
|
||||
hexToBytes(v.body_hex),
|
||||
);
|
||||
// X-Unibus-Sig is base64-standard; decode and compare hex to the Go vector.
|
||||
expect(bytesToHex(base64ToBytes(headers["X-Unibus-Sig"]))).toBe(v.sig_hex);
|
||||
expect(headers["X-Unibus-Pub"]).toBe(vectors.sign.sign_pub_hex);
|
||||
expect(headers["X-Unibus-Ts"]).toBe(v.ts);
|
||||
expect(headers["X-Unibus-Nonce"]).toBe(v.nonce);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user