|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +## For Module Consumers |
| 4 | + |
| 5 | +If you are writing code that *uses* jwx (not developing jwx itself): |
| 6 | + |
| 7 | +- **Examples**: See `examples/` directory for runnable usage patterns |
| 8 | +- **Documentation**: See `docs/` directory and package READMEs |
| 9 | +- **API Reference**: Use `go doc` or https://pkg.go.dev/github.com/lestrrat-go/jwx/v3 |
| 10 | + |
| 11 | +The rest of this document focuses on developing the jwx library itself. |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Go Version |
| 16 | + |
| 17 | +This project requires **Go 1.24.0** or later. Check `go.mod` for the exact version. |
| 18 | + |
| 19 | +## Module Path vs Physical Layout |
| 20 | + |
| 21 | +This repository uses a **flat layout** with vanity import paths. There is no physical `v3/` directory. |
| 22 | + |
| 23 | +| Branch | Module Path | Physical Root | |
| 24 | +|--------|-------------|---------------| |
| 25 | +| `develop/v3` | `github.com/lestrrat-go/jwx/v3` | `/` (repo root) | |
| 26 | + |
| 27 | +`import "github.com/lestrrat-go/jwx/v3/jwt"` → files are at `./jwt/`, not `./v3/jwt/`. |
| 28 | + |
| 29 | +## Code Generation |
| 30 | + |
| 31 | +### Immutable Rule |
| 32 | + |
| 33 | +**NEVER edit files ending in `_gen.go` directly.** These are generated files. Edit the generator sources instead. |
| 34 | + |
| 35 | +### Generated Files Pattern |
| 36 | + |
| 37 | +Files matching `*_gen.go` are generated. Examples: |
| 38 | +- `jwt/options_gen.go` |
| 39 | +- `jwt/token_gen.go` |
| 40 | +- `jws/headers_gen.go` |
| 41 | +- `jwk/rsa_gen.go` |
| 42 | +- `jwa/signature_gen.go` |
| 43 | + |
| 44 | +### Generator Locations |
| 45 | + |
| 46 | +| Generator | Location | Input Files | Output | |
| 47 | +|-----------|----------|-------------|--------| |
| 48 | +| `genoptions` | `tools/cmd/genoptions/` | `{jwa,jwe,jwk,jws,jwt}/options.yaml` | `*/options_gen.go` | |
| 49 | +| `genjwt` | `tools/cmd/genjwt/` | `tools/cmd/genjwt/objects.yml` | `jwt/*_gen.go` | |
| 50 | +| `genjws` | `tools/cmd/genjws/` | `tools/cmd/genjws/objects.yml` | `jws/*_gen.go` | |
| 51 | +| `genjwe` | `tools/cmd/genjwe/` | `tools/cmd/genjwe/objects.yml` | `jwe/*_gen.go` | |
| 52 | +| `genjwk` | `tools/cmd/genjwk/` | `tools/cmd/genjwk/objects.yml` | `jwk/*_gen.go` | |
| 53 | +| `genjwa` | `tools/cmd/genjwa/` | `tools/cmd/genjwa/objects.yml` | `jwa/*_gen.go` | |
| 54 | +| `genreadfile` | `tools/cmd/genreadfile/` | - | ReadFile helpers | |
| 55 | + |
| 56 | +### Regeneration Commands |
| 57 | + |
| 58 | +```bash |
| 59 | +# Regenerate all code |
| 60 | +make generate |
| 61 | + |
| 62 | +# Regenerate specific package |
| 63 | +make generate-jwt |
| 64 | +make generate-jws |
| 65 | +make generate-jwe |
| 66 | +make generate-jwk |
| 67 | +make generate-jwa |
| 68 | +``` |
| 69 | + |
| 70 | +## Functional Options Pattern |
| 71 | + |
| 72 | +Options are defined in `{package}/options.yaml` and generated into `{package}/options_gen.go`. |
| 73 | + |
| 74 | +Example `options.yaml` entry: |
| 75 | + |
| 76 | +```yaml |
| 77 | +options: |
| 78 | + - ident: Token |
| 79 | + interface: ParseOption |
| 80 | + argument_type: Token |
| 81 | + comment: | |
| 82 | + WithToken specifies the token instance... |
| 83 | +``` |
| 84 | +
|
| 85 | +Generates `WithToken(v Token) ParseOption` function. |
| 86 | + |
| 87 | +## Multi-Module Structure |
| 88 | + |
| 89 | +This repository contains multiple Go modules. The nested modules use `replace` directives for local development. |
| 90 | + |
| 91 | +| Module | Path | Purpose | |
| 92 | +|--------|------|---------| |
| 93 | +| Main | `./go.mod` | Core library | |
| 94 | +| Examples | `./examples/go.mod` | Usage examples | |
| 95 | +| CLI | `./cmd/jwx/go.mod` | Command-line tool | |
| 96 | +| Perf Bench | `./bench/performance/go.mod` | Performance benchmarks | |
| 97 | +| Comparison | `./bench/comparison/go.mod` | Library comparison | |
| 98 | +| Generators | `./tools/cmd/*/go.mod` | Code generators | |
| 99 | + |
| 100 | +### Local Development |
| 101 | + |
| 102 | +The `examples/go.mod` contains: |
| 103 | +```go |
| 104 | +replace github.com/lestrrat-go/jwx/v3 v3.0.0 => ../ |
| 105 | +``` |
| 106 | + |
| 107 | +No `go.work` file is present. When working across modules, either: |
| 108 | +1. Create a temporary `go.work` file |
| 109 | +2. Rely on the `replace` directives already in place |
| 110 | + |
| 111 | +## Development Commands |
| 112 | + |
| 113 | +```bash |
| 114 | +# Run all tests |
| 115 | +make test |
| 116 | +
|
| 117 | +# Run tests with specific build tags |
| 118 | +make test-goccy # Use goccy/go-json |
| 119 | +make test-es256k # Enable ES256K support |
| 120 | +make test-alltags # All optional features |
| 121 | +
|
| 122 | +# Run short/smoke tests |
| 123 | +make smoke |
| 124 | +
|
| 125 | +# Generate coverage report |
| 126 | +make cover |
| 127 | +make viewcover |
| 128 | +
|
| 129 | +# Lint |
| 130 | +make lint |
| 131 | +
|
| 132 | +# Format and tidy |
| 133 | +make imports |
| 134 | +make tidy |
| 135 | +``` |
| 136 | + |
| 137 | +### Test Script Details |
| 138 | + |
| 139 | +Tests are run via `./tools/test.sh` which iterates over: |
| 140 | +- `.` (main module) |
| 141 | +- `./examples` |
| 142 | +- `./bench/performance` |
| 143 | +- `./cmd/jwx` |
| 144 | + |
| 145 | +## Package Directory Map |
| 146 | + |
| 147 | +| Package | Responsibility | |
| 148 | +|---------|----------------| |
| 149 | +| `jwa/` | Algorithm identifiers (e.g., `RS256`, `ES384`, `A128GCM`) | |
| 150 | +| `jwk/` | JSON Web Keys - key representation and management | |
| 151 | +| `jws/` | JSON Web Signatures - `Sign()` and `Verify()` | |
| 152 | +| `jwe/` | JSON Web Encryption - `Encrypt()` and `Decrypt()` | |
| 153 | +| `jwt/` | JSON Web Tokens - claims and validation | |
| 154 | +| `jwt/openid/` | OpenID Connect ID tokens | |
| 155 | +| `transform/` | Token transformation utilities | |
| 156 | + |
| 157 | +## Relevant RFCs |
| 158 | + |
| 159 | +- RFC 7515 - JWS (JSON Web Signature) |
| 160 | +- RFC 7516 - JWE (JSON Web Encryption) |
| 161 | +- RFC 7517 - JWK (JSON Web Key) |
| 162 | +- RFC 7518 - JWA (JSON Web Algorithms) |
| 163 | +- RFC 7519 - JWT (JSON Web Token) |
| 164 | +- OpenID Connect Core 1.0 |
| 165 | + |
| 166 | +## Error Handling |
| 167 | + |
| 168 | +Sentinel errors are exposed via functions. Use `errors.Is()`: |
| 169 | + |
| 170 | +```go |
| 171 | +if errors.Is(err, jwt.TokenExpiredError()) { ... } |
| 172 | +``` |
| 173 | + |
| 174 | +| Package | Function | Meaning | |
| 175 | +|---------|----------|---------| |
| 176 | +| `jwt` | `TokenExpiredError()` | `exp` claim not satisfied | |
| 177 | +| `jwt` | `TokenNotYetValidError()` | `nbf` claim not satisfied | |
| 178 | +| `jwt` | `InvalidIssuerError()` | `iss` claim not satisfied | |
| 179 | +| `jwt` | `InvalidAudienceError()` | `aud` claim not satisfied | |
| 180 | +| `jwt` | `ValidateError()` | Generic validation failure | |
| 181 | +| `jwt` | `ParseError()` | Parse failed | |
| 182 | +| `jws` | `VerificationError()` | Signature verification failed | |
| 183 | +| `jwe` | `DecryptError()` | Decryption failed | |
| 184 | + |
| 185 | +## Testing |
| 186 | + |
| 187 | +Use `github.com/stretchr/testify/require` for assertions (not `assert`). |
| 188 | + |
| 189 | +## Build Tags |
| 190 | + |
| 191 | +| Tag | Effect | |
| 192 | +|-----|--------| |
| 193 | +| `jwx_goccy` | Use `goccy/go-json` instead of `encoding/json` | |
| 194 | +| `jwx_es256k` | Enable secp256k1/ES256K algorithm support | |
| 195 | +| `jwx_secp256k1_pem` | Enable PEM encoding for secp256k1 keys | |
| 196 | +| `jwx_asmbase64` | Use assembly-optimized base64 | |
| 197 | + |
| 198 | +## Quick Reference: Common Modifications |
| 199 | + |
| 200 | +| Task | Edit This | Then Run | |
| 201 | +|------|-----------|----------| |
| 202 | +| Add new JWT option | `jwt/options.yaml` | `make generate-jwt` | |
| 203 | +| Add new JWS header field | `tools/cmd/genjws/objects.yml` | `make generate-jws` | |
| 204 | +| Add new JWK key field | `tools/cmd/genjwk/objects.yml` | `make generate-jwk` | |
| 205 | +| Add new algorithm | `tools/cmd/genjwa/objects.yml` | `make generate-jwa` | |
| 206 | +| Modify token fields | `tools/cmd/genjwt/objects.yml` | `make generate-jwt` | |
| 207 | + |
| 208 | +## File Naming Conventions |
| 209 | + |
| 210 | +| Pattern | Meaning | |
| 211 | +|---------|---------| |
| 212 | +| `*_gen.go` | Generated code - DO NOT EDIT | |
| 213 | +| `*_test.go` | Test files | |
| 214 | +| `*_gen_test.go` | Generated tests - DO NOT EDIT | |
| 215 | +| `options.yaml` | Option definitions (input to genoptions) | |
| 216 | +| `objects.yml` | Object definitions (input to package-specific generators) | |
| 217 | + |
| 218 | +## Examples Directory |
| 219 | + |
| 220 | +Naming convention: `{package}_xxx_example_test.go` |
| 221 | +- `jwt_parse_example_test.go` |
| 222 | +- `jws_sign_example_test.go` |
| 223 | +- `jwx_example_test.go` (cross-package) |
| 224 | +- `jwx_readme_example_test.go` (cross-package, used in README) |
| 225 | +- `jwx_register_ec_and_key_example_test.go` (cross-package, key registration) |
| 226 | + |
| 227 | +Examples are included in `docs/` via autodoc markers: |
| 228 | +```markdown |
| 229 | +<!-- INCLUDE(examples/jwt_parse_example_test.go) --> |
| 230 | +<!-- END INCLUDE --> |
| 231 | +``` |
| 232 | + |
0 commit comments