Perf/multiplatform build speedup#93
Merged
Merged
Conversation
docs.yaml has been failing on main for over a week (confirmed via `gh run list` and reproduced locally with `mkdocs build --strict`), from issues introduced in a 2026-07-02 batch commit that never made it into CI's radar: - 5 broken links: one relative path to scripts/verify-phase-c.sh that doesn't resolve from its page's depth (now an absolute GitHub URL, per the project's own stated convention for source-tree references), and 4 stale heading anchors -- each assumed an em-dash in the heading slugifies to a double hyphen, but mkdocs' actual slugifier collapses it to one. Verified the real generated anchor IDs by building the site and grepping the rendered HTML rather than guessing. - 6 orphaned pages that existed on disk but were never added to mkdocs.yml's hand-curated nav, so were unreachable from the published site: architecture-separation.md, uiinteraction.md, cookbook/install-lwauth-mcp-server.md, design/cache-layer-redesign.md, design/mcp-enterprise-auth.md, design/mcp-server.md. All six are genuine, substantive documentation, not scratch notes -- added to nav under Architecture/Cookbook as appropriate. Notably, cache-layer-redesign.md is marked "Accepted ... implemented" and explicitly supersedes cache-architecture.md (which *was* in nav) -- added a superseded-by pointer at the top of the older doc so the published architecture section doesn't read as current when it isn't. `mkdocs build --strict` is fully clean after these fixes (was previously aborting with 7 warnings). Separately, closed a real accuracy gap in supply-chain.md: it documented `cosign verify ghcr.io/mikeappsec/lightweightauth:1.2.0` as if the data-plane image were signed, but neither build.yaml nor release.yaml ever actually signed it -- the example would have failed for anyone who ran it. Added cosign signing to both the stock and FIPS data-plane image builds (matching the pattern already used for the control-plane image), and added the control-plane image's own verification example, which the doc was missing entirely. Also added docs/modules/saml.md and docs/modules/scim.md -- both modules had pkg-level READMEs but no corresponding site-published reference page, unlike every other identifier module. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found by an audit cross-checking docs/modules/*.md against the actual Go source (config structs, validation logic) rather than trusting the docs' own claims: - apikey.md: hashed.entries example used field name `digest:`; the code reads `hash:` -- following the doc as written would break. - hmac.md: YAML example put identifier-specific fields at the wrong nesting level (outside `config:`); ModuleSpec only recognizes name/type/config, so the example as written wouldn't parse. Also documented an unstated validation: decoded secrets must be >= 16 bytes. - jwt.md: removed a false claim that `issuerUrl` "auto-discovers JWKS via /.well-known/openid-configuration" -- no such discovery exists in code; `jwksUrl` is unconditionally required, `issuerUrl` only pins the `iss` claim. - jwt-issue.md: the example's own `algorithm: ES256` doesn't work -- parseAlg only implements HS256/384/512 and RS256/384/512. Also fixed a commented-out EC PEM key example that would fail to parse (`key:` is HS*-only raw secret, `privateKeyFile:` is RS*-only and requires PKCS#1/PKCS#8 RSA specifically) and removed a references-section pointer to EdDSA (RFC 8037), which isn't supported either. - mtls.md: default `header` value shown in lowercase; actual Go default is `X-Forwarded-Client-Cert` (cosmetic, headers are case-insensitive, but the literal value was wrong). - dpop.md: `pinnedKeys` (proof key pinning independent of the token's own `cnf.jkt` claim) is fully implemented but was entirely undocumented. Also: docs/modules/README.md's index tables were missing 6 registered modules -- saml, scim, spicedb, and wasm already had page files, just never linked from the index; idjag and assurance had no doc page at all. Added docs/modules/idjag.md and docs/modules/assurance.md, and while fixing the tables noticed grpc-plugin (registered as identifier + authorizer + mutator) was also missing from the authorizers and mutators tables, not just wasm -- fixed both. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d-code claims
Systemic fix: the top-level AuthConfig YAML key is `response`, not
`mutators` (internal/config/config.go) - corrected across every doc
still using the old name.
Cookbook: replaced a nonexistent `lwauthctl hash-apikey`/`apikey hash`
command with the real go run + apikey.HashKey approach; rewrote
apikey-static-backend.md's fabricated config schema (keys/store/claims
don't exist - real shape is static/hashed.{entries,file,dir});
corrected fabricated lwauthctl audit/validate/diff/explain output
formats; removed a fabricated Go-template engine ({{ .Claims }}/
joinClaims) from header-add/header-remove docs - the real mutator only
supports ${sub}/${claim:x} literal substitution; fixed the admin
revoke body shape and status code (200->202) and documented that the
API has no field to target kid:/serial:/sid: revocation keys directly;
fixed wrong AuthConfig keys in install-lwauth-mcp-server.md.
Operations: removed three fake Prometheus metrics and a fake
/admin/key-states endpoint; fixed caBundlePath -> trustedCAFiles;
documented that HMAC/API-key "rotatable key with notBefore/notAfter"
support is fully implemented but never wired into the running
identifier (dead code); fixed fips.md's build-vs-verify stage
description; fixed envoy.md's stale references to the removed
lightweightauth-proxy component; fixed three fabricated test citations
and cleaned up widespread mojibake in security/v1.0-review.md.
Design: fixed README.md's garbled leftover commit-message text;
updated CONTRIBUTING.md's stale "early M0" status; fixed swapped file
names and a stale function signature in ARCHITECTURE.md; fixed
DEPLOYMENT.md's CRD count (found a real 4th CRD, PolicyBinding) and
removed Helm values that don't exist; added status notes to DESIGN.md
G10 and UIDESIGN.md clarifying the management UI / control plane is
shipped, not a future proposal.
Also added env/extraVolumes/extraVolumeMounts passthrough to the
data-plane Helm chart, mirroring the control-plane chart's existing
pattern, since several cookbook recipes already assumed it existed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Strix Security ReviewNo security issues found. Updated for Reviewed by Strix |
Continuation of the documentation audit. Every one of these 18
module docs had verified drift from the code they describe:
- composite.md: entire config schema was fabricated (mode/children
don't exist; real shape is anyOf/allOf lists, only two modes not
four)
- opa.md: fabricated bundle-loading (disk/HTTP) that doesn't exist;
wrong field name (module: vs real rego:)
- openfga.md: check.{user,relation,object} are Go text/template
strings, not CEL as claimed; fabricated decision-caching layer that
doesn't exist in the authorizer
- spicedb.md: wrong field casing (resourceID/subjectID vs real
resourceId/subjectId), fabricated template methods
(PathSegment/Header/Query), fabricated tls: block
- rbac.md: fabricated rolesFrom prefixes (source/subject don't exist),
fabricated "*" wildcard, wrong deny-reason text
- cel.md: fabricated variables: config block, wrong header binding
type, misleading runtime-error-denies claim (actually maps to 503)
- bundle.md: CLI examples used positional args that don't work (real
CLI is flag-only); fabricated Helm bundleRef/bundlePullSecret
feature; fabricated registry-inspect capability
- federation.md: reframed as implemented-but-not-wired-in (pkg/federation
is real and tested but nothing in cmd/lwauth or cmd/lwauth-controlplane
ever constructs it, and there's no federation: key on AuthConfig);
fixed missing peers[].clusterId and the federationKey yaml:"-" issue
- wasm.md: maxFuel/maxMemoryMB are parsed but never enforced (fuel
metering doesn't exist; memory is a hardcoded global 16MiB, not
per-module); missing pluginBaseDir restriction
- plugin-grpc.md: corrected the signing-default claim (TCP addresses
without insecure:true silently default to verify, not disabled)
- session.md: primary config example was entirely broken (session:
should be cookie:, ttl should be maxAge, {fromEnv:...} syntax
doesn't exist anywhere, cookieMaxBytes isn't exposed by oauth2,
wrong error sentinel for tampered cookies)
- oauth2.md: wrong field names (ttl->maxAge, refreshLeeway mis-nested,
postLogoutRedirectUrl->postLogoutPath), wrong userinfo response
shape, fabricated MemoryStore swap capability
- revocation.md: missing enabled:true, wrong negativeCache shape (real
field is flat negCacheTTL), dead maxEntries field, fake admin API
"key" field, wrong reaper interval and Valkey command, wrong metric
name/labels
- upstream.md: fabricated flat resilience: schema - budget fields are
nested inside retries:, not a separate block; corrected unverifiable
chaos-test numbers
- ratelimit.md: fabricated metric label (real counter is dedicated
lwauth_ratelimit_denied_total, not lwauth_decisions_total); fabricated
lwauthctl explain rate-limit output (explain has no rate-limit stage)
- cache-memory.md: fabricated config fields (maxEntries/defaultTtl/
decisionTtl/introspectionTtl don't exist), wrong source path, wrong
default size (10k not 50k)
- cache-valkey.md: wrong source path, wrong keyPrefix/key defaults
- configstream.md: wrong proto field numbers, stale claim about an
unresolved concurrency bug that was actually fixed in v1.1
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Final phase of the documentation audit. Every one of these package
READMEs had verified drift from the code they describe, ranging from
compile bugs in "Usage" examples to entire fabricated APIs.
Systemic bug fixed across ~15 files: module.Build{Identifier,
Authorizer,Mutator} examples had type name and instance name
swapped (real signature is (typeName, instanceName, cfg, ...) —
would fail with "unknown type" as originally written.
Severely fabricated (entire APIs/schemas invented, not just field
drift):
- buildinfo: Get()/Info struct don't exist; real API is package-level
vars + zero-arg funcs (FIPSEnabled(), GoVersion(), Summary())
- lwauthd: Run() doesn't take a context (blocks on OS signals
instead); ~20 of the real ~30 Options fields were undocumented,
4 documented fields (MetricsAddr/HealthAddr/CRDWatch/TLS) don't exist
- pkg/module: fabricated `lwauth:` struct-tag validation/defaulting
system (required/default=/min=/max=) - DecodeConfig only does
unknown-key rejection + JSON type coercion, no defaulting
- observability/audit: fabricated Emit()/AsyncSinkConfig/NewStdoutSink/
Event fields/EventType enum; fabricated sinks/sampling config schema
(the real, wired AuthConfig audit: key only has redaction/dataResidency)
- observability/tracing: fabricated NewProvider/ProviderConfig/Shutdown
(real API is two functions: Tracer(), TraceIDFromContext()); fabricated
tracing: config block, not wired into AuthConfig at all; 3 of 4 span
attribute names were wrong
- upstream: fabricated flat resilience: schema (real shape nests the
retry budget inside retries:), wrong Do() signature, fabricated
backoff jitter claim (deterministic, no jitter)
Also fixed: dead-code rotation features documented as live (apikey,
hmac, introspection, oauth2 - keyrotation.KeySet fully implemented but
never wired into factory()); mtls's CA-bundle-hot-reload claim (same
dead-code pattern - CABundleWatcher exists but factory() never uses
it); dpop's real, live pinnedKeys feature was undocumented; several
pointer-vs-value and argument-count compile bugs (session, ratelimit,
revocation, keyrotation, connpool, configstream, client/go, federation,
bundle); wrong field names/casing (plugin/grpc's hmacSecret vs secret,
federation's clusterId/revocationTtl/tlsCaFile casing, apikey's argon2
memory figure off by 1024x); a stray top-level `mutators:` key in
mutator/jwtissue and mutator/headers examples (same systemic bug fixed
throughout docs/ earlier in this audit).
pkg/observability/metrics/README.md was the one file in this batch
with zero verified drift - confirmed as the trustworthy canonical
metrics reference.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…odule docs
lwauth performs no \${VAR} environment-variable expansion anywhere in
AuthConfig - confirmed by inspecting internal/config/loader.go and
pkg/secrets: the only real secret-injection mechanisms are (1)
secretRef: "<scheme>://<path>[#field]" (currently only vault://),
resolved at compile time by resolveMapSecrets, which recursively
walks every identifier/authorizer/response module's config map plus
cache.password/cache.sharedHmacKey/revocation.password/caches[].password
specifically, or (2) templating the AuthConfig YAML itself at the
deployment-pipeline layer (Helm/Kustomize/CI) before lwauth ever
parses it. Notably rateLimit.distributed.password is NOT covered by
secretRef - that top-level block is never walked by the resolver, so
only pipeline-templating works there.
Added an accurate warning at each affected field across 20 files
(14 cookbook recipes, module docs for cache-valkey, oauth2,
oauth2-introspection, openfga, ratelimit, revocation, saml, scim,
spicedb, plus QUICKSTART.md and design/cache-architecture.md),
pointing at the real secretRef mechanism where it applies and calling
out the rateLimit exception where it doesn't.
While fixing these, found and fixed several adjacent bugs in the same
blocks: dpop-sender-binding.md's entire replayCache: config was
fabricated (real field is replayCacheSize, which is itself parsed but
never read - the actual cache comes from a named "replay" pool in the
top-level caches: block); header-stamping-identity-forwarding.md's
jwt-issue examples used audiences:/expiry: instead of the real
audience:/ttl:; spicedb-rebac-authorization.md had the same
resourceID/subjectID casing and fake tls:/PathSegment-method bugs
already fixed in the module doc, plus a consistency-level table
listing an unsupported third mode; oauth2-introspection-caching.md's
entire config schema was wrong throughout (introspectionUrl should be
url, a fabricated header:/scheme:/timeout: trio, a fabricated nested
cache: block with per-tier MaxSize fields that don't exist, wrong
resilience.retries.maxRetries vs the real .max, fabricated
lwauth_introspection_* metrics, and fake lwauthctl explain output);
docs/operations/dpop-key-rotation-runbook.md had the same
introspectionUrl bug nested inside a dpop identifier's inner config.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.