3.3 KiB
commands/files/packet-io
Purpose
Shared on-disk format helpers for the file-transport commands. Holds
the canonical packet envelope { data, signature, checksum, originalSize } (all hex-encoded scalars in JSON), the canonical
packet filename convention (packet.<6-digit-index>.bin), the manifest
filename (manifest.json), and the read/write helpers used by both
prepareFileCommand and serveFileCommand.
Lives in its own module so the format is the contract: any tool that
agrees on the envelope shape can interop with a server-keys-prepared
directory without depending on the full file-command surface. Also keeps
files.ts and serve-file.ts under the 300-line hard limit by lifting
out the duplicated serialisation code.
Features
packetFilename(index)— zero-padded 6-digit filename for deterministic sort order.bytesToHex/hexToBytes— lowercase hex, strict input validation.serializePacket(packed, originalSize)— JSON-encode aPackedDataenvelope.deserializePacket(buffer)— parse a JSON envelope back into the shapeUnpacker.unpackexpects, with a typedPACKET_INTEGRITY_FAILEDfailure on malformed JSON.readManifest(dir)/writeManifest(dir, manifest)— single-file JSON manifest I/O with directory creation.
Usage: Basic
import {
packetFilename,
serializePacket,
writeManifest,
} from "@suaveplan/server-keys/commands/files/packet-io";
const filename = packetFilename(7); // "packet.000007.bin"
const text = serializePacket(packed, 1024);
await fs.writeFile(`/tmp/prepared/${filename}`, text);
await writeManifest("/tmp/prepared", manifest);
Usage: Advanced
Round-trip a packet envelope without touching disk:
import {
deserializePacket,
serializePacket,
} from "@suaveplan/server-keys/commands/files/packet-io";
const text = serializePacket(packed, slice.byteLength);
const buf = new TextEncoder().encode(text);
const { packed: rehydrated, envelope } = deserializePacket(buf);
expect(envelope.checksum).toBe(packed.metadata.checksum);
expect(Array.from(rehydrated.data)).toEqual(Array.from(packed.data));
API Reference
MANIFEST_FILENAME = "manifest.json"packetFilename(index: number) => stringbytesToHex(bytes: Uint8Array) => stringhexToBytes(hex: string) => Uint8Array(throws on malformed input)interface SerializedPacket { data, signature, checksum, originalSize }serializePacket(packed, originalSize) => stringdeserializePacket(buffer) => { packed, envelope }writeManifest(dir, manifest) => Promise<void>readManifest(dir) => Promise<FileManifest>
Implementation Notes
readManifest translates a missing file into
SERVER_KEYS_FILE_NOT_FOUND; this is the closest match in the
registry for "the directory exists but the manifest entry is absent"
and aligns with how the keys commands report missing key files.
deserializePacket raises PACKET_INTEGRITY_FAILED on malformed JSON
rather than INVALID_INPUT because the input is a file the caller
already trusted enough to read — the failure is on the storage side,
not on a CLI option-bag boundary. The packet envelope deliberately
hex-encodes both data and signature (rather than base64) to match
the rest of the package's binary-on-disk convention and to keep the
file readable by hand.