···11+# CLAUDE.md
22+33+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44+55+## Project Overview
66+77+This is a Deno-native AT Protocol OAuth client library that provides handle-focused authentication using Web Crypto API. It's an alternative to `@atproto/oauth-client-node` built specifically to solve Node.js crypto compatibility issues in Deno environments.
88+99+**Key Design Philosophy:**
1010+- Handle-focused (accepts `alice.bsky.social` only, not DIDs or URLs)
1111+- Uses Slingshot resolver by default with multiple fallbacks
1212+- Web Crypto API for cross-platform compatibility (no Node.js crypto)
1313+- DPoP (Demonstrating Proof of Possession) authentication with ES256 keys
1414+1515+## Development Commands
1616+1717+```bash
1818+# Run all tests with required permissions
1919+deno test --allow-net --allow-read
2020+2121+# Run a specific test file
2222+deno test --allow-net --allow-read tests/session_test.ts
2323+2424+# Type checking
2525+deno check mod.ts
2626+deno check **/*.ts
2727+2828+# Format code
2929+deno fmt # check formatting
3030+deno fmt --check # verify formatting
3131+deno fmt <file> # format specific file
3232+3333+# Lint code
3434+deno lint
3535+3636+# Run full CI suite (format, lint, check, test)
3737+deno task ci
3838+```
3939+4040+## Architecture
4141+4242+### Core Components
4343+4444+1. **OAuthClient** (`src/client.ts`) - Main OAuth flow orchestration
4545+ - Handles authorization URL generation with PKCE
4646+ - Token exchange and refresh with DPoP
4747+ - Pushed Authorization Request (PAR) support
4848+ - Concurrency-safe token refresh with per-session locks
4949+5050+2. **Session** (`src/session.ts`) - Authenticated session management
5151+ - Token lifecycle management (access + refresh tokens)
5252+ - DPoP-authenticated request handling with automatic nonce retry
5353+ - 5-minute expiration buffer for token refresh
5454+ - Serializable session state
5555+5656+3. **Handle Resolvers** (`src/resolvers.ts`) - AT Protocol handle resolution
5757+ - `SlingshotResolver` (default): Slingshot → Bluesky API → direct lookup fallback chain
5858+ - `DirectoryResolver`: Bluesky API only
5959+ - `CustomResolver`: User-provided resolution function
6060+ - OAuth endpoint discovery from PDS metadata
6161+6262+4. **DPoP** (`src/dpop.ts`) - Proof of Possession implementation
6363+ - ES256 (ECDSA P-256) key generation using Web Crypto API
6464+ - JWT proof generation with `jsr:@panva/jose` (not npm version)
6565+ - Automatic nonce handling on 401 challenges
6666+6767+5. **Storage** (`src/storage.ts`) - Session persistence abstractions
6868+ - `MemoryStorage`: In-memory with TTL support
6969+ - `SQLiteStorage`: Deno SQLite backend example
7070+ - `LocalStorage`: Browser/localStorage compatible
7171+ - All storage is async with TTL support
7272+7373+6. **Error Handling** (`src/errors.ts`) - Typed error hierarchy
7474+ - All errors extend `OAuthError` base class
7575+ - Specific error types for each failure mode
7676+ - Error chaining with `cause` support
7777+7878+### Critical Implementation Details
7979+8080+**Web Crypto API Usage:**
8181+- MUST use `crypto.subtle.generateKey()` with `{ name: "ECDSA", namedCurve: "P-256" }`
8282+- NEVER use Node.js crypto APIs - they don't work in Deno
8383+- Import jose from `jsr:@panva/jose` NOT `npm:jose`
8484+8585+**DPoP Authentication:**
8686+- Every token request requires DPoP proof with ES256 signature
8787+- Access token hash (`ath` claim) required for authenticated requests
8888+- Servers may respond with 400/401 + `DPoP-Nonce` header requiring retry
8989+9090+**Concurrency Safety:**
9191+- `OAuthClient.restore()` uses `refreshLocks` Map to prevent duplicate refresh requests
9292+- Multiple concurrent restore calls for same session wait on single refresh operation
9393+9494+**Token Refresh:**
9595+- Sessions considered expired if token expires within 5 minutes
9696+- Refresh may or may not rotate refresh token (server-dependent)
9797+- Refresh failures throw typed errors: `RefreshTokenExpiredError`, `NetworkError`, etc.
9898+9999+## Testing
100100+101101+- All tests use `Deno.test()` with nested `t.step()` for organization
102102+- Use `jsr:@std/assert@1` for assertions
103103+- Tests require `--allow-net` for resolver/OAuth endpoint tests
104104+- Tests are isolated - use helper functions like `createTestSessionData()`
105105+- NO mocking of external services - tests use real AT Protocol endpoints
106106+107107+## Security Considerations
108108+109109+- OAuth state validation prevents CSRF attacks (10-minute TTL on PKCE data)
110110+- PKCE flow protects against authorization code interception
111111+- DPoP binds tokens to specific key pairs
112112+- No credentials should ever be committed to the repository
113113+114114+## Publishing
115115+116116+- Package published to JSR (not npm)
117117+- Version in `deno.json` must be updated before publishing
118118+- Run `deno publish` from repository root
119119+- See `CHANGELOG.md` for version history
120120+121121+## Common Tasks
122122+123123+**Adding a new error type:**
124124+1. Create class extending appropriate base in `src/errors.ts`
125125+2. Add JSDoc with `@example` showing usage
126126+3. Export from `mod.ts`
127127+4. Add test case in `tests/errors_test.ts`
128128+129129+**Adding a new storage backend:**
130130+1. Implement `OAuthStorage` interface in `src/storage.ts`
131131+2. Handle TTL and expiration logic
132132+3. Export from `mod.ts`
133133+4. Add test coverage in `tests/storage_test.ts`
134134+135135+**Modifying OAuth flow:**
136136+- Changes to `client.ts` likely require testing against real AT Protocol servers
137137+- Ensure PKCE data cleanup happens even on errors
138138+- Maintain per-session refresh lock semantics