first commit
This commit is contained in:
70
docs/networking/drive/drive-template/src/cdn/upload-token.md
Normal file
70
docs/networking/drive/drive-template/src/cdn/upload-token.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# upload-token
|
||||
|
||||
## Purpose
|
||||
|
||||
Implements `CdnUploadToken` issuance and verification (DTP-19). Tokens authorise a single chunk write to the CDN and carry a server signature over the canonical bytes plus a content-hash binding so the CDN can refuse to store anything other than what the server attested.
|
||||
|
||||
## Features
|
||||
|
||||
- `signUploadToken(token, serverSecretKey, options?)` — fills `expiresAt` from the injected `Clock` + `ttlMs` and signs.
|
||||
- `verifyUploadToken(token, serverPubkeyHex, clock)` — full check: expiry, hex decode, Ed25519 verify.
|
||||
- `canonicalSerializeUploadToken(token)` — deterministic byte-level encoding shared by issuer and verifier.
|
||||
- `UPLOAD_TOKEN_TTL_DEFAULT_MS` (60 s), `UPLOAD_TOKEN_TTL_MAX_MS` (300 s) — pinned constants the spec dashboards can reference.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
signUploadToken,
|
||||
verifyUploadToken,
|
||||
} from "@suaveplan/drive-template";
|
||||
|
||||
const tok = signUploadToken(
|
||||
{
|
||||
fileId,
|
||||
chunkId: 0,
|
||||
chunkHash: "blake3:abc",
|
||||
uploaderPubkey: peerId,
|
||||
} as never,
|
||||
serverSecretKey,
|
||||
{ clock: () => Date.now() }
|
||||
);
|
||||
verifyUploadToken(tok, serverPubkeyHex, () => Date.now());
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
DTP_CODES,
|
||||
DriveTemplateError,
|
||||
signUploadToken,
|
||||
verifyUploadToken,
|
||||
} from "@suaveplan/drive-template";
|
||||
|
||||
async function handleCdnUpload(token, body) {
|
||||
try {
|
||||
verifyUploadToken(token, serverPubkeyHex, () => Date.now());
|
||||
} catch (err) {
|
||||
if (err instanceof DriveTemplateError && err.code === DTP_CODES.CDN_BAD_SIGNATURE) {
|
||||
return new Response("forbidden", { status: 403 });
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
if (`blake3:${blake3Hex(body)}` !== token.chunkHash) {
|
||||
return new Response("hash mismatch", { status: 422 });
|
||||
}
|
||||
return store(token.fileId, token.chunkId, body);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `signUploadToken(token, serverSecretKey, options?: { clock: Clock; ttlMs?: number }): CdnUploadToken`.
|
||||
- `verifyUploadToken(token: CdnUploadToken, serverPubkeyHex: string, clock: Clock): true`.
|
||||
- `canonicalSerializeUploadToken(token: Omit<CdnUploadToken, "serverSignature">): Uint8Array`.
|
||||
- Constants: `UPLOAD_TOKEN_TTL_DEFAULT_MS`, `UPLOAD_TOKEN_TTL_MAX_MS`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The TTL upper bound is enforced at sign time to prevent a misconfigured caller from minting tokens that outlive the request that asked for them. Verification trusts `@noble/curves` to return `false` for malformed signatures — there's no separate try/catch, which removes a dead branch and keeps the success path branch-free in the hot path. The CDN-side replay protection (the `(fileId, chunkId, nonce)` short LRU mentioned in DTP-19) lives in the CDN service rather than this module; the protocol only mandates the token shape.
|
||||
Reference in New Issue
Block a user