Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
# `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 a `PackedData`
envelope.
- `deserializePacket(buffer)` — parse a JSON envelope back into the
shape `Unpacker.unpack` expects, with a typed `PACKET_INTEGRITY_FAILED`
failure on malformed JSON.
- `readManifest(dir)` / `writeManifest(dir, manifest)` — single-file
JSON manifest I/O with directory creation.
## Usage: Basic
```ts
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:
```ts
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) => string`
- `bytesToHex(bytes: Uint8Array) => string`
- `hexToBytes(hex: string) => Uint8Array` (throws on malformed input)
- `interface SerializedPacket { data, signature, checksum, originalSize }`
- `serializePacket(packed, originalSize) => string`
- `deserializePacket(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.