first commit
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# chunk-verifier-validator
|
||||
|
||||
## Purpose
|
||||
|
||||
Bridges the structural `ChunkVerifier` shape from `@suaveplan/packer`
|
||||
into the canonical `ChunkValidator` strategy. The packer's verifier
|
||||
returns a discriminated result (`{ ok: true, metadata }` or
|
||||
`{ ok: false, code, message }`); this adapter coerces the failure
|
||||
branch into the canonical `StreamsChunkQueueError(VALIDATION_FAILED)`
|
||||
with the packer's diagnostic preserved in `context.verifierCode`.
|
||||
|
||||
## Features
|
||||
|
||||
- Accepts any structurally compatible verifier (`ChunkVerifierLike`),
|
||||
so adapter tests don't need a real packer instance.
|
||||
- Preserves the verifier's `code` + `message` inside the typed error's
|
||||
`context` for downstream observability.
|
||||
- Returns the input bytes unchanged on success — `ChunkVerifier`
|
||||
validates the wire frame but does not modify it.
|
||||
- Falls back to a sensible default `code`/`message` if the verifier
|
||||
omits them, keeping the contract uniform.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { ChunkVerifier } from "@suaveplan/packer";
|
||||
import { ChunkVerifierValidator } from "@suaveplan/streams-chunk-queue";
|
||||
|
||||
const verifier = new ChunkVerifier(serverPubKey);
|
||||
const queue = new StreamingChunkQueue({
|
||||
fileId: 1n,
|
||||
totalChunks: 5n,
|
||||
validator: new ChunkVerifierValidator(verifier),
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import type { ChunkVerifierLike } from "@suaveplan/streams-chunk-queue";
|
||||
|
||||
const hsmVerifier: ChunkVerifierLike = {
|
||||
verify(bytes) {
|
||||
return hsmSignatureVerify(bytes)
|
||||
? { ok: true }
|
||||
: { ok: false, code: "HSM.REJECTED", message: "HSM rejected" };
|
||||
},
|
||||
};
|
||||
const validator = new ChunkVerifierValidator(hsmVerifier);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `ChunkVerifierLike`
|
||||
|
||||
```ts
|
||||
interface ChunkVerifierLike {
|
||||
verify(bytes: Uint8Array): {
|
||||
ok: boolean;
|
||||
code?: string;
|
||||
message?: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### `ChunkVerifierValidator`
|
||||
|
||||
```ts
|
||||
new ChunkVerifierValidator(verifier: ChunkVerifierLike)
|
||||
```
|
||||
|
||||
Implements `ChunkValidator`. On `ok: true` returns
|
||||
`{ data: <input bytes> }`. On `ok: false` rejects with
|
||||
`StreamsChunkQueueError(VALIDATION_FAILED)` whose
|
||||
`context.chunkId` is the offending chunk id and `context.verifierCode`
|
||||
carries the verifier's own code.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The adapter does not mutate `data` because `ChunkVerifier.verify` only
|
||||
checks the bytes — it does not strip a header. If a future verifier
|
||||
returns transformed bytes, the adapter contract can be extended without
|
||||
breaking callers (the success branch already returns `{ data }`). The
|
||||
adapter is intentionally synchronous on the verifier side (the wrapper
|
||||
itself returns a `Promise` to satisfy the strategy interface), so a
|
||||
fast in-process verifier does not allocate an extra microtask. The
|
||||
structural typing of `ChunkVerifierLike` means hardware-backed
|
||||
verifiers, mock test verifiers, and the real packer class are all
|
||||
interchangeable.
|
||||
@@ -0,0 +1,93 @@
|
||||
# validator
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Declares the `ChunkValidator` strategy interface and provides the
|
||||
`NoopChunkValidator` default. Validation is the second stage of the
|
||||
chunk pipeline (between ingestion and decryption). The strategy
|
||||
interface lets callers plug in any verification primitive — Ed25519
|
||||
signature checks, HMAC tags, custom integrity schemes — without the
|
||||
core queue needing to know the cryptographic details.
|
||||
|
||||
## Features
|
||||
|
||||
- `ChunkValidator` interface with a single async `validate` method.
|
||||
- `NoopChunkValidator` default that passes every chunk through
|
||||
unchanged.
|
||||
- `noopChunkValidator` singleton shorthand for callers that don't want
|
||||
to instantiate.
|
||||
- Adapters live in sibling modules (`chunk-verifier-validator.ts`).
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { noopChunkValidator } from "@suaveplan/streams-chunk-queue";
|
||||
|
||||
const queue = new StreamingChunkQueue({
|
||||
fileId: 1n,
|
||||
totalChunks: 5n,
|
||||
validator: noopChunkValidator,
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import type { ChunkValidator } from "@suaveplan/streams-chunk-queue";
|
||||
|
||||
class HmacValidator implements ChunkValidator {
|
||||
constructor(private readonly key: Uint8Array) {}
|
||||
async validate(chunkId: bigint, data: Uint8Array) {
|
||||
const tagLen = 32;
|
||||
const tag = data.slice(0, tagLen);
|
||||
const body = data.slice(tagLen);
|
||||
if (!(await hmacVerify(this.key, body, tag))) {
|
||||
throw new Error(`HMAC failed for chunk ${chunkId}`);
|
||||
}
|
||||
return { data: body };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `ChunkValidator`
|
||||
|
||||
```ts
|
||||
interface ChunkValidator {
|
||||
validate(chunkId: bigint, data: Uint8Array): Promise<ValidationOutcome>;
|
||||
}
|
||||
```
|
||||
|
||||
Implementations MUST throw on failure. The thrown error is wrapped by
|
||||
the queue into `StreamsChunkQueueError(VALIDATION_FAILED)`.
|
||||
|
||||
### `ValidationOutcome`
|
||||
|
||||
`{ data: Uint8Array }`. The returned bytes supersede the inline
|
||||
ciphertext — useful when validation strips a header or signature.
|
||||
|
||||
### `NoopChunkValidator`
|
||||
|
||||
Concrete class implementing `ChunkValidator` as a pass-through.
|
||||
|
||||
### `noopChunkValidator`
|
||||
|
||||
Singleton instance of `NoopChunkValidator`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The interface is async even for synchronous validators because all
|
||||
real-world validators end up wanting WebCrypto / native crypto, which
|
||||
is async. Forcing the contract to be uniformly async means the queue
|
||||
can `await` without per-implementation branches. The `data` return
|
||||
value is intentionally writable — adapters may strip a signature
|
||||
header before passing bytes downstream, so the queue stores the
|
||||
adapter-returned bytes (not the original wire frame) in its in-memory
|
||||
map. A noop validator is included so the queue can call
|
||||
`validator.validate` unconditionally without checking for a
|
||||
configured strategy.
|
||||
Reference in New Issue
Block a user