Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# AGENTS.md

Privacy Pass TypeScript implementation. Compliant with:

- [RFC 9576](https://datatracker.ietf.org/doc/html/rfc9576) — Privacy Pass Architecture (roles, deployment models, security considerations)
- [RFC 9578](https://datatracker.ietf.org/doc/html/rfc9578) — Privacy Pass Issuance Protocol
- [draft-hendrickson-privacypass-public-metadata](https://datatracker.ietf.org/doc/draft-hendrickson-privacypass-public-metadata/) — Public Metadata (Partially Blind RSA)
- [draft-ietf-privacypass-batched-tokens](https://datatracker.ietf.org/doc/draft-ietf-privacypass-batched-tokens/) — Batched Tokens

Reference implementations to cross-check behavior when debugging protocol issues.

1. [pat-go](https://github.com/cloudflare/pat-go) (Cloudflare's Go implementation),
2. [privacypass](https://github.com/raphaelrobert/privacypass) (Raphael Robert's Rust implementation).

## Token Types

| Type | Module | Crypto |
| ---------------------------- | ---------------- | ------------------- |
| Public-Verifiable | `publicVerif` | Blind RSA |
| Public-Verifiable + Metadata | `publicVerif` | Partially Blind RSA |
| Private-Verifiable | `privateVerif` | VOPRF (P-384) |
| Batched (generic) | `genericBatched` | Any of above |

## Structure

```
src/
index.ts # exports, TOKEN_TYPES registry
pub_verif_token.ts # Blind RSA / Partially Blind RSA
priv_verif_token.ts # VOPRF
generic_batched_token.ts
issuance.ts # fetch helpers, MediaType enum
auth_scheme/ # WWW-Authenticate / Authorization parsing (RFC 9110)
test/
*.test.ts # vitest tests
test_data/ # JSON test vectors (Go, Rust implementations)
examples/ # usage examples — run with `npm run examples`
```

## Requirements

- **Node.js ≥20**
- **npm** (not pnpm/yarn)
- **ESM-only** — uses `"type": "module"`, all imports need `.js` extensions

## Commands

```bash
npm ci # install dependencies
npm run build # tsc -b (required before test/bench/examples)
npm run test # vitest
npm run lint # eslint
npm run format # prettier
npm run examples # run examples (builds first)
npm run bench # run benchmarks (builds first)
```

See [README.md](README.md) for full usage.

## Code Patterns

### Serialization

All wire types implement `serialize(): Uint8Array` and `static deserialize(bytes: Uint8Array): T`.

```typescript
class TokenRequest {
serialize(): Uint8Array {
/* ... */
}
static deserialize(tokenType: TokenTypeEntry, bytes: Uint8Array): TokenRequest {
/* ... */
}
}
```


### Role Classes

Per [RFC 9576](https://datatracker.ietf.org/doc/html/rfc9576) (Privacy Pass Architecture), each token type exposes three roles:

- `Client` — creates token requests, finalizes tokens
- `Issuer` — signs blinded requests
- `Origin` — creates challenges, verifies tokens

### Type Imports/Exports

ESLint enforces `consistent-type-imports` and `consistent-type-exports`:

```typescript
// use `import type` for type-only imports
import type { TokenChallenge } from './auth_scheme/private_token.js';

// use `export type` for type-only exports
export type { TokenReq, TokenRes };
```

### Unused Variables

Prefix with underscore to satisfy `noUnusedLocals`/`noUnusedParameters`:

```typescript
function example(_unusedParam: string): void {
/* ... */
}
```

## Testing

### Test Vectors

Vectors live in `test/test_data/` as JSON. Sources:

- Go: RFC 9578 reference implementation
- Rust: [raphaelrobert/privacypass](https://github.com/raphaelrobert/privacypass)

### Adding Test Vectors

1. Generate vectors from another implementation (see [Interop Wiki](https://github.com/raphaelrobert/privacypass/wiki/Interop))
2. Place JSON in `test/test_data/` with naming convention: `{token_type}_{source}.json`
3. Import in relevant `*.test.ts` and add to test array

Example from `pub_verif_token.test.ts`:

```typescript
import vectorsGo from './test_data/pub_verif_rfc9578.go.json';
import vectorsRust from './test_data/pub_verif_rfc9578.rust.json';
const vectors = [...vectorsGo, ...vectorsRust];

describe.each(vectors)('PublicVerifiable-Vector-%#', (v: Vectors) => {
/* ... */
});
```

### Test Helpers

- `testSerialize(Type, instance)` — roundtrip serialize/deserialize
- `testSerializeType(tokenType, Type, instance)` — same, with token type param
- `hexToUint8`, `uint8ToHex` — hex conversion utilities

## Platform Constraints

**Partially Blind RSA verification does not work in browsers.** WebCrypto implementations reject the large public exponents required by the protocol. See [Chromium bug](https://issues.chromium.org/issues/340178598), [Firefox bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1896444).

Workaround: verify tokens server-side only.

## Extensions

Token requests can carry extensions per [draft-hendrickson-privacypass-public-metadata](https://datatracker.ietf.org/doc/draft-hendrickson-privacypass-public-metadata/). See `Extensions` class in `src/auth_scheme/private_token.ts`. This is draft-stage; API may change.

## IETF Compliance

This library tracks IETF specifications. When drafts advance (new versions or RFC publication), update the implementation accordingly. Check the [IETF Privacy Pass WG](https://datatracker.ietf.org/wg/privacypass/documents/) for current document status.

## Dependencies

- [@cloudflare/blindrsa-ts](https://github.com/cloudflare/blindrsa-ts) — Blind RSA, Partially Blind RSA
- [@cloudflare/voprf-ts](https://github.com/cloudflare/voprf-ts) — VOPRF