From c725d59ffb7d188eff55fe035af1b81502737b7c Mon Sep 17 00:00:00 2001 From: fullsend-code <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 09:30:02 +0000 Subject: [PATCH 1/3] docs(#6307): add mintcore contributor guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add docs/contributing/mintcore.md documenting the shipped mintcore architecture: deploy targets and load sites, platform accessors (mintEnv, mintHTTP), load-site construction pattern, WASM-safe and WASM-unsafe patterns, PEM injection model, interfaces vs accessors, embed sync rules, and explicitly rejected patterns (niladic factories, wire_*.go, mintPEM, NewPlatformHandler). Link the guide from AGENTS.md topic-specific guidance table and from docs/architecture.md (standalone mint deployment bullet). docs/contributing/go-code.md WASM section already covers the accessor and load-site construction invariants consistently — no changes needed. Closes #6307 --- AGENTS.md | 1 + docs/architecture.md | 2 +- docs/contributing/mintcore.md | 385 ++++++++++++++++++++++++++++++++++ 3 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 docs/contributing/mintcore.md diff --git a/AGENTS.md b/AGENTS.md index e7c57385bf..515ede5ccf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,6 +28,7 @@ Detailed guidance lives in `docs/contributing/` and topic-specific guides under | File | When to read | |------|-------------| | [Go Code](docs/contributing/go-code.md) | Changing Go code under `cmd/` or `internal/` — covers mint sync, coverage, vet, e2e tests, concurrency testing, suite-timeout policy, WASM binary size constraints, and preferring `go run` for the CLI | +| [Mintcore Architecture](docs/contributing/mintcore.md) | Changing `internal/mintcore/`, `cmd/mint-wasm/`, `cmd/mint/`, or `internal/mint/` — covers platform accessors, load-site construction, WASM-safe wiring, and rejected patterns | | [Behaviour Testing](docs/guides/dev/behaviour-testing.md) | Modifying behaviour test repo provisioning, fork handling, or workflow dispatch — covers forge API constraints (`auto_init`, fork name derivation, Actions readiness, CI timeout budgeting) | | [Workflow Contracts](docs/contributing/workflow-contracts.md) | Changing GHA reusable workflows — covers dispatch sync, secret/input threading across installation-mode chains, and review rules | | [Shell Scripting](docs/contributing/shell-scripting.md) | Writing or reviewing shell scripts — covers `gh api --paginate` pitfalls, jq patterns, and stdout contamination in command substitution | diff --git a/docs/architecture.md b/docs/architecture.md index 4ea0155444..96628f51ca 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -239,7 +239,7 @@ Identity is not the same as trust. An agent's identity lets it authenticate to e - Cross-org mint authorization: workflows may request tokens for a different org via optional `target_org` when the target org installs the role App and sets `FULLSEND_FOREIGN__REPOS` ([ADR 0060](ADRs/0060-cross-org-mint-authorization-via-org-variables.md)). Repo-level `FULLSEND_FOREIGN__REPOS` variables enable per-repo foreign grants (scoped to the specific target repo) and intra-org cross-repo access for per-repo callers, with disjoint authorization boundaries from org-level grants — repo-level for repo-scoped requests, org-level for installation-wide requests ([ADR 0083](ADRs/0083-repo-level-foreign-allow-list.md)). - Mint `repos` scope: foreign mints with `repos: ["*"]` require an org-level FOREIGN grant; foreign mints with specific repos require per-repo FOREIGN grants on each requested repo (org-level grants are not consulted for repo-scoped requests). Per-repo callers (repo in `PER_REPO_WIF_REPOS`) must list exactly the requesting repository unless authorized by repo-level FOREIGN grants for other repos. Per-org callers (org in `ALLOWED_ORGS`, repo not in `PER_REPO_WIF_REPOS`) get org-mode shapes: `.fullsend` callers may use any non-empty validated list; other callers may use `[.fullsend]` or `{self,.fullsend}`. Same-org installation-wide tokens are denied ([ADR 0077](ADRs/0077-mint-repos-scope-hardening.md), simplified in [ADR 0078](ADRs/0078-simplified-mint-authorization-policy.md)). - Workflow-host allow-list: `WORKFLOW_HOST_REPOS` controls which repos may host workflows calling the mint for per-repo and public-mode callers (default: `fullsend-ai/fullsend`). Per-org callers hard-wire to `{org}/.fullsend` and upstream. Public mode is not special-cased — it uses the same per-repo validation path with `WORKFLOW_HOST_REPOS` and the basename allowlist. This separates caller enrollment from workflow-host trust ([ADR 0082](ADRs/0082-workflow-host-allow-list.md)). -- Standalone mint deployment: `cmd/mint/` provides a self-contained HTTP server that uses direct JWKS verification and filesystem PEM storage instead of GCP infrastructure. It shares the `internal/mintcore/` library with the GCF mint and adds support for custom role permissions and a fallback proxy to an upstream mint. Custom role permissions live in mintcore (not `cmd/mint/`) so that `RolePermissionsFor`, `HasRole`, and `CreateInstallationToken` return a unified view without callers needing to distinguish built-in from custom roles. The GCF mint never calls `RegisterCustomRolePermissions`, so the code is inert there. See the [standalone mint guide](guides/infrastructure/standalone-mint.md). +- Standalone mint deployment: `cmd/mint/` provides a self-contained HTTP server that uses direct JWKS verification and filesystem PEM storage instead of GCP infrastructure. It shares the `internal/mintcore/` library with the GCF mint and adds support for custom role permissions and a fallback proxy to an upstream mint. Custom role permissions live in mintcore (not `cmd/mint/`) so that `RolePermissionsFor`, `HasRole`, and `CreateInstallationToken` return a unified view without callers needing to distinguish built-in from custom roles. The GCF mint never calls `RegisterCustomRolePermissions`, so the code is inert there. See the [standalone mint guide](guides/infrastructure/standalone-mint.md). For mintcore internals (platform accessors, load-site construction, WASM constraints), see the [mintcore contributor guide](contributing/mintcore.md). - Hosted public community mint: steady-state deployment on Cloudflare Workers (JWKS + WAF + single ops console), with interim GCP Cloud Function acceptable until the Worker port is production-ready. Trust policy (`ALLOWED_ORGS=*`, upstream-only workflow provenance) is in [ADR 0059](ADRs/0059-public-mint-mode-with-wildcard-allowlists.md); deployment, edge security, monitoring, and phasing are in [ADR 0068](ADRs/0068-public-community-mint-architecture.md). Enrollment is installing the shared Apps—no per-org mint env registration ([#1145](https://github.com/fullsend-ai/fullsend/issues/1145)). - Named privilege levels: each role defines ordered named levels (`read`, `write`), where each level's permissions are a superset of preceding levels. `read` for built-in roles is derived by downgrading `*:write` permissions to their `read` counterparts. The mint API accepts an optional `level` field (default `read`); omitting it produces narrower tokens than the current behavior. `write` is defined as the current max permission set for each built-in role. `CUSTOM_ROLE_PERMISSIONS` auto-detects a multi-level JSON shape alongside the existing flat format, with mixed format supported per role. The harness `privilege_levels` flag maps run-stages to levels; omitting it defaults to `write`, preserving backward compatibility for existing harness configurations ([ADR 0073](ADRs/0073-named-mint-privilege-levels.md)). diff --git a/docs/contributing/mintcore.md b/docs/contributing/mintcore.md new file mode 100644 index 0000000000..a8e596cc12 --- /dev/null +++ b/docs/contributing/mintcore.md @@ -0,0 +1,385 @@ +# Mintcore Architecture + +This guide describes the **shipped** wiring of `internal/mintcore/` — the +shared token-minting library used by three deploy targets. It covers +platform accessors, load-site construction, WASM constraints, and the +patterns that keep the Cloudflare Worker binary within size limits. + +Read this when you are changing files under `internal/mintcore/`, +`cmd/mint-wasm/`, `cmd/mint/`, or `internal/mint/`. + +## Deploy targets + +Mintcore is shared by three deploy targets. Each target has its own +**load site** (entrypoint) that constructs the verifier and PEM accessor +and passes them into `NewHandler`. + +| Target | Load site | Verifier | PEM accessor | +|--------|-----------|----------|--------------| +| **GCF** (Cloud Function) | `internal/mint` | `NewSTSVerifier` | `NewGCPSecretPEMAccessor` | +| **Cloudflare Worker** | `cmd/mint-wasm` (`mintcoreInitMint`) | `NewJWKSVerifier` | `NewHostPEMAccessor` | +| **Standalone** | `cmd/mint` | `NewJWKSVerifier` | `NewFilesystemPEMAccessor` | + +A fourth consumer, **devmint** (used in e2e tests), also constructs +`NewJWKSVerifier` and passes its own PEM accessor via the same +`NewHandler` interface. + +## Layering + +``` +internal/mintcore/ shared library — all mint logic + mintconsts/ compile-time constants (OIDCAudience) +internal/mint/ GCF load site (has its own go.mod) +cmd/mint/ standalone HTTP server load site (has its own go.mod) +cmd/mint-wasm/ WASM entrypoint for CF Worker (imports mintcore) +internal/dispatch/cf/ + workersrc/ CF Worker TypeScript adapter (I/O only) + gcf/mintsrc/ embedded copies for GCF deployment (.embed files) +``` + +**internal/mintcore/** contains all token-minting logic: request +parsing, OIDC verification, claims validation, authorization (org, +workflow-ref, repos scope), GitHub App token creation, and status +endpoints. It compiles to both native (`!js`) and WASM (`js`) targets. + +**Entrypoints** (`internal/mint`, `cmd/mint`, `cmd/mint-wasm`) are thin. +They construct the appropriate `OIDCVerifier` + `PEMAccessor` for their +platform, call `NewHandler(pemAccessor, verifier)`, and serve HTTP. +No mint logic lives in entrypoints. + +**The CF Worker TypeScript adapter** (`workersrc/src/index.ts`) handles +I/O only — Worker secrets, host fetch, Fetch Request/Response mapping. +It calls `mintcoreInitMint` and `mintcoreHandleFetch` registered on +`globalThis` by the Go WASM bridge in `cmd/mint-wasm`. + +## Platform accessors + +Two package-internal accessor functions abstract platform I/O inside +mintcore: + +### `mintEnv(key string) string` + +Reads environment/configuration values. + +| Build tag | Implementation | File | +|-----------|----------------|------| +| `!js` (native) | `os.Getenv(key)` | `env.go` | +| `js` (WASM) | JS callback registered via `RegisterEnv` | `env_js.go` | + +`NewHandler` reads all configuration variables (`ROLE_APP_IDS`, +`ALLOWED_ORGS`, `ALLOWED_WORKFLOW_FILES`, `PER_REPO_WIF_REPOS`, +`WORKFLOW_HOST_REPOS`, `CUSTOM_ROLE_PERMISSIONS`, `ALLOWED_ROLES`) +via `mintEnv` at construction time. + +### `mintHTTP(req *http.Request) (*http.Response, error)` + +Executes outbound HTTP requests (GitHub API calls). + +| Build tag | Implementation | File | +|-----------|----------------|------| +| `!js` (native) | Cached `*http.Client` with 30s timeout | `http_client.go` | +| `js` (WASM) | JS fetch callback registered via `RegisterHTTP` | `http_client_js.go` | + +`mintHTTP(req)` is called at **request time** inside handler and +verifier files (`github.go`, `jwks_verifier.go`, `sts_verifier.go`, +`gcp_pem.go`). It is never called during construction. + +### Registration on WASM + +The CF Worker calls `RegisterEnv` and `RegisterHTTP` once during +`mintcoreInitMint` — before `NewHandler` reads configuration. Only +`cmd/mint-wasm/main.go` calls these functions; native entrypoints do +not need to (and cannot — the functions are behind `//go:build js`). + +### Who may call `RegisterEnv` / `RegisterHTTP` + +Only the WASM entrypoint (`cmd/mint-wasm`). These are one-shot +registration functions, not something load sites or tests should call. +Tests use `t.Setenv` for environment variables and +`SetMintHTTPForTest(t, fake)` for HTTP overrides. + +## Load-site construction + +Each entrypoint builds the verifier and PEM accessor appropriate for +its platform, then passes them into `NewHandler`: + +```go +// cmd/mint (standalone) +verifier, _ := mintcore.NewJWKSVerifier(mintcore.JWKSVerifierConfig{ + IssuerURL: "https://token.actions.githubusercontent.com", +}) +pemAccessor, _ := mintcore.NewFilesystemPEMAccessor(os.Getenv("PEM_DIR")) +handler, _ := mintcore.NewHandler(pemAccessor, verifier) +``` + +```go +// internal/mint (GCF) +verifier, _ := mintcore.NewSTSVerifier(mintcore.STSVerifierConfig{ + GCPProjectNum: gcpProjectNum, + WIFPoolName: wifPoolName, + DefaultWIFProvider: defaultWIFProvider, + PerRepoWIFRepos: perRepoWIFRepos, +}) +pemAccessor := mintcore.NewGCPSecretPEMAccessor(gcpProjectNum) +handler, _ := mintcore.NewHandler(pemAccessor, verifier) +``` + +```go +// cmd/mint-wasm (CF Worker) +mintcore.RegisterEnv(getEnvFn) +mintcore.RegisterHTTP(fetchFn) +pemAccessor, _ := mintcore.NewHostPEMAccessor(pemFn) +verifier, _ := mintcore.NewJWKSVerifier(mintcore.JWKSVerifierConfig{ + IssuerURL: "https://token.actions.githubusercontent.com", +}) +handler, _ := mintcore.NewHandler(pemAccessor, verifier) +``` + +Key properties: + +- **No `getEnv` or `HTTPDoer` parameters** on `NewHandler`. The handler + reads config via `mintEnv` and makes HTTP calls via `mintHTTP` + internally. +- **No factories passed into `NewHandler`.** The handler receives + constructed interfaces, not factory functions. +- **Verifier configs use plain data** (strings, maps, booleans). The + OIDC audience is the compile-time constant `mintconsts.OIDCAudience`, + applied inside verifier constructors — load sites do not pass it. + +## WASM-unsafe patterns + +These patterns increase the WASM binary size or cause deadlocks in the +Worker runtime. **Do not use them in `internal/mintcore/`.** + +### Closures and function values in config structs + +```go +// BAD: closure captures http.Client's entire dependency graph +type VerifierConfig struct { + DoHTTP func(*http.Request) (*http.Response, error) +} +``` + +Closure capture pulls the entire dependency graph of the captured +variables into the WASM binary. A `func(string) string` for env lookups +or a `func(*http.Request) (*http.Response, error)` for HTTP inflates the +binary even if the function body is trivial, because the compiler must +include all transitively reachable types. + +### `mintEnv` / `mintHTTP` inside verifier constructors + +```go +// BAD: constructor-time HTTP pulls net/http into the call graph +// at init, and on WASM the JS callbacks may not be registered yet +func NewJWKSVerifierFromEnv() (*JWKSVerifier, error) { + issuer := mintEnv("OIDC_ISSUER") // constructor-time env read + // ... +} +``` + +Verifier constructors run during `mintcoreInitMint`. Using `mintHTTP` +in a constructor tries to make HTTP calls before the event loop is +available, risking deadlocks. Using `mintEnv` in a constructor is less +dangerous but couples construction to environment state rather than +explicit config. + +The shipped pattern: constructors take plain config structs → +`mintHTTP(req)` is called at **request time** in verifier and handler +methods. + +### Niladic `*FromEnv` factories + +```go +// BAD: hides which env vars are read, couples to runtime state +func NewJWKSVerifierFromEnv() (*JWKSVerifier, error) { ... } +func NewSTSVerifierFromEnv() (*STSVerifier, error) { ... } +``` + +These factories were part of the original design in #6304 and were +**dropped** after the WASM bisect in PR #6308. They hide configuration +behind runtime state, making it impossible to construct verifiers with +different configs in tests without mutating the environment. + +### Threading `http.Client` through entrypoints + +```go +// BAD: passing http.Client from entrypoint into verifier config +func NewHandler(pem PEMAccessor, v OIDCVerifier, client *http.Client) { ... } +``` + +This pulls `net/http` into the constructor's dependency graph at the +entrypoint level. Instead, `mintHTTP(req)` is called directly at use +sites inside mintcore. + +### `wire_*.go` / `NewPlatformHandler()` + +Deploy-time wiring files (`wire_gcf.go`, `wire_standalone.go`, +`wire_wasm.go`) and a single `NewPlatformHandler()` factory were part of +the original design in #6304. **They were not implemented** — #6306 was +closed as an unnecessary optimization. Each load site constructs +`NewHandler` directly with explicit arguments. + +### `mintPEM` / `RegisterPEM` + +A package-internal PEM accessor (`mintPEM`) analogous to `mintEnv` and +`mintHTTP` was considered but **not implemented**. PEM stays an injected +`PEMAccessor` interface passed to `NewHandler`. The three PEM +implementations (`GCPSecretPEMAccessor`, `FilesystemPEMAccessor`, +`HostPEMAccessor`) are constructed at load sites and are not swappable +via registration. + +## WASM-safe patterns + +These are the patterns used in shipped code. Follow them when modifying +mintcore. + +### `Register*` once at bootstrap + +On WASM, the CF Worker calls `RegisterEnv(fn)` and `RegisterHTTP(fn)` +once during `mintcoreInitMint`, before constructing the handler. Native +platforms do not call these — `mintEnv` delegates to `os.Getenv` and +`mintHTTP` uses a cached `*http.Client` automatically via build tags. + +### Constructors take plain data + +Verifier configs contain only strings, maps, and booleans — no function +values, no interfaces, no closures: + +```go +type JWKSVerifierConfig struct { + IssuerURL string +} + +type STSVerifierConfig struct { + GCPProjectNum string + WIFPoolName string + DefaultWIFProvider string + PerRepoWIFRepos map[string]bool + // optional fields... +} +``` + +### Request-time `mintHTTP(req)` + +HTTP calls happen at request time, not construction time. Files that +call `mintHTTP` include: + +- `github.go` — GitHub App installation token creation +- `jwks_verifier.go` — JWKS key fetching +- `sts_verifier.go` — GCP STS token exchange +- `gcp_pem.go` — GCP Secret Manager access + +This is intentional: on WASM, the JS event loop must be free for +Promises to settle. Constructor-time HTTP would deadlock. + +### Handler reads config via `mintEnv` + +`NewHandler` reads `ROLE_APP_IDS`, `ALLOWED_ORGS`, and other +configuration variables once via `mintEnv` at construction time. +This works because `RegisterEnv` has already been called by the +time `NewHandler` runs — the pattern is: register → construct → +serve. + +## PEM remains injected + +PEM access uses the `PEMAccessor` interface at the `NewHandler` +boundary. There is no `mintPEM` accessor and no `RegisterPEM`. + +| PEM implementation | Platform | Storage | +|-------------------|----------|---------| +| `GCPSecretPEMAccessor` | GCF | GCP Secret Manager | +| `FilesystemPEMAccessor` | Standalone | Local directory (`PEM_DIR`) | +| `HostPEMAccessor` | CF Worker (WASM) | Worker secrets via JS callback | + +Each load site constructs the appropriate accessor and passes it to +`NewHandler`. Tests can pass any `PEMAccessor` implementation. + +## Interfaces vs accessors + +Mintcore uses two patterns for dependency injection: + +**Package-internal accessors** (`mintEnv`, `mintHTTP`) for production +environment lookups and HTTP — these are implementation details of +mintcore, not part of its public API. They exist because every file in +mintcore needs env/HTTP access, and threading interfaces through every +function signature would be impractical. Tests override them with +`t.Setenv` and `SetMintHTTPForTest(t, fake)`. + +**Interfaces** (`OIDCVerifier`, `PEMAccessor`) at the `NewHandler` +boundary — these are the public contract between load sites and +mintcore. Load sites choose which implementation to construct; mintcore +does not know or care which platform it is running on. + +```go +type OIDCVerifier interface { + Verify(ctx context.Context, rawToken string) (*Claims, error) +} + +type PEMAccessor interface { + AccessPEM(ctx context.Context, role string) ([]byte, error) +} +``` + +## Embed sync and `make wasm-build` + +### Embedded copies for GCF + +Mintcore files are embedded for Cloud Function deployment at +`internal/dispatch/gcf/mintsrc/mintcore/*.embed`. When changing any +file in `internal/mintcore/`, sync it to the corresponding `.embed` +file. The `lint-mint-embed-sync` pre-commit hook enforces this. + +When **adding a new file** to `internal/mintcore/`: + +1. Create the `.embed` copy in + `internal/dispatch/gcf/mintsrc/mintcore/`. +2. If the file is included in the GCF bundle (no build tag, or + `//go:build !js`), add it to `embeddedMintFiles` in + `internal/dispatch/gcf/provisioner.go` and to the `go:embed` + directive. +3. If the file is NOT included in the GCF bundle (Worker-only + `//go:build js` files, or standalone-mint-only files), add it to + `gcfSkip` in `TestEmbeddedMintSource_MatchesOriginal` in + `provisioner_test.go`. + +Current `gcfSkip` entries: `env_js.go`, `fetch_js.go`, +`http_client_js.go`, `pem_js.go` (all `//go:build js`), and +`file_pem.go` (standalone-mint-only, `//go:build !js`). + +### WASM binary size gate + +The compiled WASM binary must stay within Cloudflare Workers size +limits. Run `make wasm-build` after any change to `internal/mintcore/` +or `cmd/mint-wasm/` to verify: + +| Tier | Gzip limit | Makefile behavior | +|------|-----------|-------------------| +| Workers Free | 3 MB | Warning | +| Workers Paid | 10 MB | Hard fail | + +Keep the WASM dependency graph minimal. The Go WASM compiler includes +the transitive closure of all referenced packages — small-looking +changes can cause large binary size increases. Avoid importing heavy +packages (`net/http`, `crypto/x509`, cloud SDKs) in files that are +WASM-compiled. Use build tags (`//go:build js` / `//go:build !js`) to +isolate platform-specific implementations. + +## Rejected patterns + +The following were part of the original mintcore design (#6304) and +are **not part of the shipped architecture**. Do not implement them. + +| Pattern | Status | Why | +|---------|--------|-----| +| `NewJWKSVerifierFromEnv()` | Rejected | Niladic factory; hides config, couples to runtime state | +| `NewSTSVerifierFromEnv()` | Rejected | Same as above | +| `VerifierFactory` interface | Rejected | Unnecessary abstraction layer | +| `wire_gcf.go` / `wire_standalone.go` / `wire_wasm.go` | Not implemented | #6306 closed as unnecessary; load sites construct directly | +| `NewPlatformHandler()` | Not implemented | See `wire_*.go` above | +| `mintPEM` / `RegisterPEM` | Not implemented | PEM stays an injected `PEMAccessor` interface | +| `func(string) string` in config structs | Rejected | WASM binary size trap; closures capture dependency graphs | + +These patterns were evaluated during the WASM bisect (PR #6308) and +dropped. #6306 was closed without implementation. If you encounter +references to these patterns in the parent epic (#6304), note that +the epic body predates the shipped design. From 351d42a1c3f30cc789af7f4acefa047cba786b9e Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 09:59:28 +0000 Subject: [PATCH 2/3] fix(docs): address review feedback on PR #6515 - Fix layering diagram in mintcore.md: split internal/dispatch/cf/ and internal/dispatch/gcf/ into separate top-level entries (they are sibling directories, not nested) - Update gcfSkip count in go-code.md from three to five entries, adding env_js.go and http_client_js.go to match provisioner_test.go - Remove preamble paragraph from mintcore.md to match convention of other docs/contributing/ files (scope is in AGENTS.md table) Addresses review feedback on #6515 --- docs/contributing/go-code.md | 2 +- docs/contributing/mintcore.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/contributing/go-code.md b/docs/contributing/go-code.md index 6e3ee3be50..fef0b12664 100644 --- a/docs/contributing/go-code.md +++ b/docs/contributing/go-code.md @@ -17,7 +17,7 @@ The `internal/mintcore/` module is shared between the mint and devmint. Its file **When adding a new file to `internal/mintcore/`:** 1. **Create the `.embed` copy:** Place it in `internal/dispatch/gcf/mintsrc/mintcore/` (required for all files — `lint-mint-embed-sync` enforces this). 2. **Register in `embeddedMintFiles`:** If the file will be included in the GCF bundle — either no build tag (e.g., `config.go`) or `//go:build !js` (e.g., `sts_verifier.go`, `gcp_pem.go`, `wif.go`) — add it to `embeddedMintFiles` in `internal/dispatch/gcf/provisioner.go` and to the `go:embed` directive. -3. **Add to `gcfSkip`:** If the file should NOT be in the GCF bundle — Worker-only files (`//go:build js`) or standalone-mint-only files — add it to the `gcfSkip` map in `TestEmbeddedMintSource_MatchesOriginal` in `provisioner_test.go` instead of `embeddedMintFiles`. The three current entries are `fetch_js.go` and `pem_js.go` (Worker-only, `//go:build js`) and `file_pem.go` (standalone-mint-only, `//go:build !js`). +3. **Add to `gcfSkip`:** If the file should NOT be in the GCF bundle — Worker-only files (`//go:build js`) or standalone-mint-only files — add it to the `gcfSkip` map in `TestEmbeddedMintSource_MatchesOriginal` in `provisioner_test.go` instead of `embeddedMintFiles`. The five current entries are `env_js.go`, `fetch_js.go`, `http_client_js.go`, and `pem_js.go` (Worker-only, `//go:build js`) and `file_pem.go` (standalone-mint-only, `//go:build !js`). **Dispatch workflows:** See [Workflow Contracts](workflow-contracts.md) for dispatch sync rules, secret/input threading across installation-mode chains, and review instructions. diff --git a/docs/contributing/mintcore.md b/docs/contributing/mintcore.md index a8e596cc12..6aa678ec36 100644 --- a/docs/contributing/mintcore.md +++ b/docs/contributing/mintcore.md @@ -5,9 +5,6 @@ shared token-minting library used by three deploy targets. It covers platform accessors, load-site construction, WASM constraints, and the patterns that keep the Cloudflare Worker binary within size limits. -Read this when you are changing files under `internal/mintcore/`, -`cmd/mint-wasm/`, `cmd/mint/`, or `internal/mint/`. - ## Deploy targets Mintcore is shared by three deploy targets. Each target has its own @@ -34,7 +31,8 @@ cmd/mint/ standalone HTTP server load site (has its own go.mod) cmd/mint-wasm/ WASM entrypoint for CF Worker (imports mintcore) internal/dispatch/cf/ workersrc/ CF Worker TypeScript adapter (I/O only) - gcf/mintsrc/ embedded copies for GCF deployment (.embed files) +internal/dispatch/gcf/ + mintsrc/ embedded copies for GCF deployment (.embed files) ``` **internal/mintcore/** contains all token-minting logic: request From c61b66e2eb76e762317827746aefd56e9942ed85 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 10:54:41 +0000 Subject: [PATCH 3/3] fix(docs): drop mentions of unused/dropped patterns from mintcore guide Remove sections documenting rejected/unimplemented patterns (niliadic *FromEnv factories, threading http.Client, wire_*.go, mintPEM/RegisterPEM, and the "Rejected patterns" summary table). These describe historical design decisions that were never shipped and add noise for contributors reading the guide for orientation. Co-Authored-By: Claude Opus 4.6 --- AGENTS.md | 2 +- docs/contributing/mintcore.md | 70 +++-------------------------------- 2 files changed, 6 insertions(+), 66 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 515ede5ccf..4c326405c2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,7 +28,7 @@ Detailed guidance lives in `docs/contributing/` and topic-specific guides under | File | When to read | |------|-------------| | [Go Code](docs/contributing/go-code.md) | Changing Go code under `cmd/` or `internal/` — covers mint sync, coverage, vet, e2e tests, concurrency testing, suite-timeout policy, WASM binary size constraints, and preferring `go run` for the CLI | -| [Mintcore Architecture](docs/contributing/mintcore.md) | Changing `internal/mintcore/`, `cmd/mint-wasm/`, `cmd/mint/`, or `internal/mint/` — covers platform accessors, load-site construction, WASM-safe wiring, and rejected patterns | +| [Mintcore Architecture](docs/contributing/mintcore.md) | Changing `internal/mintcore/`, `cmd/mint-wasm/`, `cmd/mint/`, or `internal/mint/` — covers platform accessors, load-site construction, and WASM-safe wiring | | [Behaviour Testing](docs/guides/dev/behaviour-testing.md) | Modifying behaviour test repo provisioning, fork handling, or workflow dispatch — covers forge API constraints (`auto_init`, fork name derivation, Actions readiness, CI timeout budgeting) | | [Workflow Contracts](docs/contributing/workflow-contracts.md) | Changing GHA reusable workflows — covers dispatch sync, secret/input threading across installation-mode chains, and review rules | | [Shell Scripting](docs/contributing/shell-scripting.md) | Writing or reviewing shell scripts — covers `gh api --paginate` pitfalls, jq patterns, and stdout contamination in command substitution | diff --git a/docs/contributing/mintcore.md b/docs/contributing/mintcore.md index 6aa678ec36..f8bb71de85 100644 --- a/docs/contributing/mintcore.md +++ b/docs/contributing/mintcore.md @@ -167,10 +167,11 @@ include all transitively reachable types. ### `mintEnv` / `mintHTTP` inside verifier constructors ```go -// BAD: constructor-time HTTP pulls net/http into the call graph -// at init, and on WASM the JS callbacks may not be registered yet -func NewJWKSVerifierFromEnv() (*JWKSVerifier, error) { +// BAD: constructor-time env/HTTP pulls dependencies into the call +// graph at init, and on WASM the JS callbacks may not be registered yet +func NewJWKSVerifier() (*JWKSVerifier, error) { issuer := mintEnv("OIDC_ISSUER") // constructor-time env read + resp, _ := mintHTTP(req) // constructor-time HTTP call // ... } ``` @@ -185,47 +186,6 @@ The shipped pattern: constructors take plain config structs → `mintHTTP(req)` is called at **request time** in verifier and handler methods. -### Niladic `*FromEnv` factories - -```go -// BAD: hides which env vars are read, couples to runtime state -func NewJWKSVerifierFromEnv() (*JWKSVerifier, error) { ... } -func NewSTSVerifierFromEnv() (*STSVerifier, error) { ... } -``` - -These factories were part of the original design in #6304 and were -**dropped** after the WASM bisect in PR #6308. They hide configuration -behind runtime state, making it impossible to construct verifiers with -different configs in tests without mutating the environment. - -### Threading `http.Client` through entrypoints - -```go -// BAD: passing http.Client from entrypoint into verifier config -func NewHandler(pem PEMAccessor, v OIDCVerifier, client *http.Client) { ... } -``` - -This pulls `net/http` into the constructor's dependency graph at the -entrypoint level. Instead, `mintHTTP(req)` is called directly at use -sites inside mintcore. - -### `wire_*.go` / `NewPlatformHandler()` - -Deploy-time wiring files (`wire_gcf.go`, `wire_standalone.go`, -`wire_wasm.go`) and a single `NewPlatformHandler()` factory were part of -the original design in #6304. **They were not implemented** — #6306 was -closed as an unnecessary optimization. Each load site constructs -`NewHandler` directly with explicit arguments. - -### `mintPEM` / `RegisterPEM` - -A package-internal PEM accessor (`mintPEM`) analogous to `mintEnv` and -`mintHTTP` was considered but **not implemented**. PEM stays an injected -`PEMAccessor` interface passed to `NewHandler`. The three PEM -implementations (`GCPSecretPEMAccessor`, `FilesystemPEMAccessor`, -`HostPEMAccessor`) are constructed at load sites and are not swappable -via registration. - ## WASM-safe patterns These are the patterns used in shipped code. Follow them when modifying @@ -281,7 +241,7 @@ serve. ## PEM remains injected PEM access uses the `PEMAccessor` interface at the `NewHandler` -boundary. There is no `mintPEM` accessor and no `RegisterPEM`. +boundary. | PEM implementation | Platform | Storage | |-------------------|----------|---------| @@ -361,23 +321,3 @@ changes can cause large binary size increases. Avoid importing heavy packages (`net/http`, `crypto/x509`, cloud SDKs) in files that are WASM-compiled. Use build tags (`//go:build js` / `//go:build !js`) to isolate platform-specific implementations. - -## Rejected patterns - -The following were part of the original mintcore design (#6304) and -are **not part of the shipped architecture**. Do not implement them. - -| Pattern | Status | Why | -|---------|--------|-----| -| `NewJWKSVerifierFromEnv()` | Rejected | Niladic factory; hides config, couples to runtime state | -| `NewSTSVerifierFromEnv()` | Rejected | Same as above | -| `VerifierFactory` interface | Rejected | Unnecessary abstraction layer | -| `wire_gcf.go` / `wire_standalone.go` / `wire_wasm.go` | Not implemented | #6306 closed as unnecessary; load sites construct directly | -| `NewPlatformHandler()` | Not implemented | See `wire_*.go` above | -| `mintPEM` / `RegisterPEM` | Not implemented | PEM stays an injected `PEMAccessor` interface | -| `func(string) string` in config structs | Rejected | WASM binary size trap; closures capture dependency graphs | - -These patterns were evaluated during the WASM bisect (PR #6308) and -dropped. #6306 was closed without implementation. If you encounter -references to these patterns in the parent epic (#6304), note that -the epic body predates the shipped design.