# 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): 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.