From 1f95ce5ac4e41f8a771d03dc7211b4272ae87bb2 Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Fri, 6 Feb 2026 14:41:03 +0000 Subject: [PATCH] feat!: deviceId as hex, not z32 --- README.md | 11 +++++------ package.json | 3 +-- src/index.ts | 3 +-- src/lib/download-request.ts | 3 +-- src/lib/utils.ts | 3 +-- src/routes/map-shares.ts | 3 +-- test/__snapshots__/map-shares.test.ts.snap | 2 +- test/helpers.ts | 5 ++--- test/map-shares.test.ts | 15 +++++++-------- types/z32.d.ts | 13 ------------- 10 files changed, 20 insertions(+), 41 deletions(-) delete mode 100644 types/z32.d.ts diff --git a/README.md b/README.md index 5297343..dca3471 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,7 @@ Content-Type: application/json { "mapId": "custom", - "receiverDeviceId": "kmx8sejfn..." // z32-encoded public key + "receiverDeviceId": "a1b2c3d4..." // hex-encoded public key } ``` @@ -326,7 +326,7 @@ POST /downloads Content-Type: application/json { - "senderDeviceId": "z32-encoded-public-key", + "senderDeviceId": "a1b2c3d4e5f6...", "shareId": "abc123...", "mapShareUrls": ["http://192.168.1.100:9090/mapShares/abc123..."], "estimatedSizeBytes": 12345678 @@ -370,7 +370,7 @@ POST /mapShares/{shareId}/decline Content-Type: application/json { - "senderDeviceId": "z32-encoded-public-key", + "senderDeviceId": "a1b2c3d4e5f6...", "mapShareUrls": ["http://192.168.1.100:9090/mapShares/abc123..."], "reason": "disk_full" | "user_rejected" | "other reason" } @@ -385,7 +385,6 @@ Called on the receiver's local server. The server handles making the P2P request ```javascript import { createServer } from '@comapeo/map-server' import Hypercore from 'hypercore' -import z32 from 'z32' const deviceAKeyPair = Hypercore.keyPair() const serverA = createServer({ @@ -398,7 +397,7 @@ const serverA = createServer({ const { localPort } = await serverA.listen() // Device B's public key (exchanged via your discovery mechanism) -const deviceBId = 'kmx8sejfn...' // z32-encoded +const deviceBId = 'a1b2c3d4e5f6...' // hex-encoded // Create share const res = await fetch(`http://127.0.0.1:${localPort}/mapShares`, { @@ -559,7 +558,7 @@ All error responses follow this format: | `DOWNLOAD_SHARE_DECLINED` | 409 | Cannot download a share that was declined | | `DOWNLOAD_SHARE_NOT_PENDING` | 409 | Cannot download a share that is not pending | | `ABORT_NOT_DOWNLOADING` | 409 | Cannot abort a download that is not in progress | -| `INVALID_SENDER_DEVICE_ID` | 400 | The sender device ID is not a valid z32-encoded public key | +| `INVALID_SENDER_DEVICE_ID` | 400 | The sender device ID is not a valid hex-encoded public key | ### Generic Errors diff --git a/package.json b/package.json index 27a54b8..f1dbf11 100644 --- a/package.json +++ b/package.json @@ -73,8 +73,7 @@ "secret-stream-http": "^1.0.1", "styled-map-package": "^4.0.1", "typebox": "^1.0.61", - "typed-event-target": "^3.4.0", - "z32": "^1.1.0" + "typed-event-target": "^3.4.0" }, "bundleDependencies": [ "@whatwg-node/server" diff --git a/src/index.ts b/src/index.ts index 236e0a7..425ea8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,6 @@ import { Agent, createServer as createSecretStreamServer, } from 'secret-stream-http' -import z32 from 'z32' import { Context } from './context.js' import { fetchAPI } from './lib/fetch-api.js' @@ -79,7 +78,7 @@ export function createServer(options: ServerOptions) { serverAdapter(req, res, { isLocalhost: false, // @ts-expect-error - the types for this are too hard and making them work would not add any type safety. - remoteDeviceId: z32.encode(req.socket.remotePublicKey), + remoteDeviceId: Buffer.from(req.socket.remotePublicKey).toString('hex'), }) }) const secretStreamServer = createSecretStreamServer(remoteHttpServer, { diff --git a/src/lib/download-request.ts b/src/lib/download-request.ts index 6bdb112..c3b94fa 100644 --- a/src/lib/download-request.ts +++ b/src/lib/download-request.ts @@ -1,5 +1,4 @@ import { Agent as SecretStreamAgent } from 'secret-stream-http' -import z32 from 'z32' import { TypedEventTarget } from '../lib/event-target.js' import type { DownloadCreateParams } from '../routes/downloads.js' @@ -46,7 +45,7 @@ export class DownloadRequest extends TypedEventTarget< } let remotePublicKey: Uint8Array try { - remotePublicKey = z32.decode(this.#state.senderDeviceId) + remotePublicKey = Buffer.from(this.#state.senderDeviceId, 'hex') } catch { throw new errors.INVALID_SENDER_DEVICE_ID( `Invalid sender device ID: ${this.#state.senderDeviceId}`, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 070dc71..fbd514b 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,7 +2,6 @@ import crypto from 'node:crypto' import { randomBytes } from 'crypto' import type { SMPStyle } from 'styled-map-package' -import z32 from 'z32' import type { BBox } from '../types.js' @@ -33,7 +32,7 @@ export function getErrorCode(maybeError: unknown) { export function noop() {} export function generateId() { - return z32.encode(randomBytes(8)) + return randomBytes(8).toString('hex') } export function getOrInsert(map: Map, key: K, value: V): V { diff --git a/src/routes/map-shares.ts b/src/routes/map-shares.ts index a0f81cb..60d185c 100644 --- a/src/routes/map-shares.ts +++ b/src/routes/map-shares.ts @@ -7,7 +7,6 @@ import { } from 'secret-stream-http' import { Type as T, type Static } from 'typebox' import { Compile } from 'typebox/compile' -import z32 from 'z32' import type { Context } from '../context.js' import { errors, StatusError } from '../lib/errors.js' @@ -151,7 +150,7 @@ export function MapSharesRouter( throw new errors.INVALID_REQUEST() } const { senderDeviceId, mapShareUrls, reason } = parsedBody - const remotePublicKey = z32.decode(senderDeviceId) + const remotePublicKey = Buffer.from(senderDeviceId, 'hex') const keyPair = ctx.getKeyPair() let response: Response | undefined // The sharer could have multiple IPs for different network interfaces, and diff --git a/test/__snapshots__/map-shares.test.ts.snap b/test/__snapshots__/map-shares.test.ts.snap index 8b7fd45..752153c 100644 --- a/test/__snapshots__/map-shares.test.ts.snap +++ b/test/__snapshots__/map-shares.test.ts.snap @@ -13,7 +13,7 @@ exports[`Map Shares and Downloads > Map Shares > should create a map share 1`] = "mapName": "OSM Bright", "maxzoom": 6, "minzoom": 0, - "receiverDeviceId": "tkrq8zmwb8a3m9k15csu3q17qmfgqnp9dskbrg9uq1rydpyxp7qy", + "receiverDeviceId": "8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c", "status": "pending", } `; diff --git a/test/helpers.ts b/test/helpers.ts index 8e064f5..6775c51 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -8,7 +8,6 @@ import { http, HttpResponse } from 'msw' import { setupServer } from 'msw/node' import { Agent as SecretStreamAgent } from 'secret-stream-http' import type { TestContext } from 'vitest' -import z32 from 'z32' import type { ServerOptions } from '../src/index.js' import { createServer } from '../src/index.js' @@ -133,7 +132,7 @@ export async function startServers( remoteBaseUrl: sender.remoteBaseUrl, remotePort: sender.remotePort, keyPair: senderKeyPair, - deviceId: z32.encode(senderKeyPair.publicKey), + deviceId: Buffer.from(senderKeyPair.publicKey).toString('hex'), eventsPath: (id: string) => `/mapShares/${id}/events`, } @@ -146,7 +145,7 @@ export async function startServers( remoteBaseUrl: receiver.remoteBaseUrl, localPort: receiver.localPort, keyPair: receiverKeyPair, - deviceId: z32.encode(receiverKeyPair.publicKey), + deviceId: Buffer.from(receiverKeyPair.publicKey).toString('hex'), customMapPath: receiver.customMapPath, eventsPath: (id: string) => `/downloads/${id}/events`, } diff --git a/test/map-shares.test.ts b/test/map-shares.test.ts index 72d4355..3c81d6a 100644 --- a/test/map-shares.test.ts +++ b/test/map-shares.test.ts @@ -13,7 +13,6 @@ import { Agent as SecretStreamAgent, } from 'secret-stream-http' import { describe, it, expect } from 'vitest' -import z32 from 'z32' import type { MapShareState } from '../src/types.js' import { @@ -151,7 +150,7 @@ describe('Map Shares and Downloads', () => { // Create a third device (another receiver) const receiver2KeyPair = SecretStreamAgent.keyPair(Buffer.alloc(32, 2)) - const receiver2DeviceId = z32.encode(receiver2KeyPair.publicKey) + const receiver2DeviceId = Buffer.from(receiver2KeyPair.publicKey).toString('hex') // Create share for first receiver const share1 = await createShare().json() @@ -1102,7 +1101,7 @@ describe('Map Shares and Downloads', () => { // Create a third device with different keys const wrongKeyPair = SecretStreamAgent.keyPair() - const wrongDeviceId = z32.encode(wrongKeyPair.publicKey) + const wrongDeviceId = Buffer.from(wrongKeyPair.publicKey).toString('hex') // Create a share for a different device const { shareId: shareId2 } = await sender @@ -1132,7 +1131,7 @@ describe('Map Shares and Downloads', () => { // Create a third device with different keys const wrongKeyPair = SecretStreamAgent.keyPair() - const wrongDeviceId = z32.encode(wrongKeyPair.publicKey) + const wrongDeviceId = Buffer.from(wrongKeyPair.publicKey).toString('hex') // Create a share for wrongDeviceId const { shareId } = await sender @@ -1185,7 +1184,7 @@ describe('Map Shares and Downloads', () => { // Create a third device const wrongKeyPair = SecretStreamAgent.keyPair() - const wrongDeviceId = z32.encode(wrongKeyPair.publicKey) + const wrongDeviceId = Buffer.from(wrongKeyPair.publicKey).toString('hex') // Create a share for wrongDeviceId const { shareId } = await sender @@ -1494,7 +1493,7 @@ describe('Map Shares and Downloads', () => { const response = await receiver.post('downloads', { json: { - senderDeviceId: 'not-a-valid-z32-encoded-public-key', + senderDeviceId: 'not-a-valid-hex-encoded-public-key', shareId: 'test-share', mapShareUrls: ['http://127.0.0.1:1/mapShares/test-share'], estimatedSizeBytes: 1000, @@ -1509,8 +1508,8 @@ describe('Map Shares and Downloads', () => { it('should reject download with sender device ID of wrong length', async (t) => { const { receiver } = await startServers(t) - // Valid z32 encoding but only 16 bytes instead of 32 - const shortKey = z32.encode(new Uint8Array(16)) + // Valid hex encoding but only 16 bytes instead of 32 + const shortKey = Buffer.from(new Uint8Array(16)).toString('hex') const response = await receiver.post('downloads', { json: { diff --git a/types/z32.d.ts b/types/z32.d.ts deleted file mode 100644 index c69cf17..0000000 --- a/types/z32.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -declare module 'z32' { - /** - * Encode a number to a z32 string - * @param num Number to encode - * @returns z32 encoded string - */ - declare function encode(buf: Buffer | Uint8Array): string - declare function decode( - str: string, - out?: Buffer | Uint8Array, - ): Buffer | Uint8Array - export { encode, decode } -}