first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
# inline-hint
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Implements DTP-25 (drive-plugin-chunks-inline-sig). Provides the signed `InlineHint` shape used by `FileLocation.storageHints` entries with `kind: 'inline'`, the Ed25519 signing helper that produces them, the verifier that the drive runs before returning inline bytes, and the legacy-detection / upgrade helpers that let pre-DTP-25 hints (no `sig` field) round-trip safely. The signing input is the canonical concatenation `fileId ‖ bytes`, which binds the signature to one file so a replay onto a different fileId would fail verification.
## Features
- `InlineHint` — `{ kind: 'inline'; bytes: Uint8Array; sig: Uint8Array }` for the DTP-25 wire format.
- `LegacyInlineHint` — `{ kind: 'inline'; bytes: Uint8Array }` for backward-compat reads.
- `signInlineHint` — Ed25519-sign over `fileId ‖ bytes` under the drive's secret key.
- `verifyInlineHint` — verify and return bytes; throws `CHUNKS.INLINE_SIG_INVALID` on failure (catch arm covers verifier exceptions too).
- `isLegacyInlineHint` — type-guard distinguishing legacy from signed shapes.
- `upgradeLegacyInlineHint` — sign a legacy hint with the drive's key so the next write rewrites it as `InlineHint`.
- `inlineSigningInput` — exposes the canonical concatenation so other layers can re-derive the bytes that are signed.
- Cause-chain preservation: verifier exceptions are wrapped as `ChunksError` with the original error attached as `cause`.
## Usage: Basic Example
```ts
import {
signInlineHint,
verifyInlineHint,
} from "@suaveplan/drive-plugin-chunks";
import { sha256 } from "@suaveplan/crypto-hash";
const fileId = sha256(plaintext);
const hint = signInlineHint(fileId, plaintext, driveSecretKey);
// later, on read:
const bytes = verifyInlineHint(hint, fileId, drivePublicKey);
console.log(bytes.byteLength);
```
## Usage: Advanced Example
```ts
import {
isLegacyInlineHint,
upgradeLegacyInlineHint,
verifyInlineHint,
type AnyInlineHint,
} from "@suaveplan/drive-plugin-chunks";
function readInline(
hint: AnyInlineHint,
fileId: Uint8Array,
driveSecretKey: Uint8Array,
drivePublicKey: Uint8Array,
onLegacyWarning: () => void
): Uint8Array {
if (isLegacyInlineHint(hint)) {
onLegacyWarning(); // emit drive:inline:legacy
const upgraded = upgradeLegacyInlineHint(
hint,
fileId,
driveSecretKey
);
// Persist `upgraded` on the next write; for the current read we
// can return the bytes directly because legacy hints predate the
// sig requirement.
return upgraded.bytes;
}
return verifyInlineHint(hint, fileId, drivePublicKey);
}
```
## API Reference
- `signInlineHint(fileId: Uint8Array, bytes: Uint8Array, driveSecretKey: Uint8Array): InlineHint` — Ed25519-sign `fileId ‖ bytes` and return the signed hint.
- `verifyInlineHint(hint: InlineHint, fileId: Uint8Array, drivePublicKey: Uint8Array): Uint8Array` — verify and return `hint.bytes`. Throws `ChunksError(CHUNKS.INLINE_SIG_INVALID, ...)` if the signature does not verify or the verifier raises.
- `isLegacyInlineHint(hint: AnyInlineHint): hint is LegacyInlineHint` — true when the hint omits `sig`.
- `upgradeLegacyInlineHint(legacy: LegacyInlineHint, fileId: Uint8Array, driveSecretKey: Uint8Array): InlineHint` — sign the legacy bytes under the drive's key.
- `inlineSigningInput(fileId: Uint8Array, bytes: Uint8Array): Uint8Array` — canonical signing input.
- `InlineHint`, `LegacyInlineHint`, `AnyInlineHint`, `StorageHint`, `FileLocation` — wire types.
## Implementation Notes
The verifier uses `@noble/curves` Ed25519 via `@suaveplan/crypto-curve/ed25519`. `verify()` is implemented to never throw on malformed inputs in the noble path, but the catch arm exists for engine-substitution scenarios where a custom verifier may throw — the `injectable verifyFn` pattern is intentionally not surfaced here because the chunks plugin always uses noble. The `CHUNKS.INLINE_SIG_INVALID` code is registered through `defineCodes("CHUNKS", ...)` in `error-codes.ts`. Domain separation is provided by including `fileId` in the signing input; this is sufficient because every inline-hint context already has a unique fileId, so the signature cannot be replayed across files even if an attacker controls one inline body.