# Config for the `link-check` task, part of the `check` aggregate. # lychee makes live network requests, so this is tuned to tolerate transient failures (retries, # 429-tolerance, disk cache) and to skip reserved hosts + non-prose trees. # Retry transient failures; cap each request so a hung host can't stall the run. max_retries = 3 retry_wait_time = 2 timeout = 20 # Cache results on disk (.lycheecache) so reruns skip recently-checked URLs. cache = true max_cache_age = "2d" # Treat GitHub's anonymous throttle (429) as reachable. # The link is valid -- we're just rate-limited. accept = ["200..=299", "429"] # Skip loopback and private/reserved IPs, which are not real external links. # This covers localhost, 127.0.0.1, and private/reserved IPs (e.g. the 10.0.0.1:9000 test fixture). exclude_loopback = true exclude_private = true # Exclude node_modules explicitly so vendored package docs don't surface as link errors. # It is gitignored, but lychee's gitignore-respect doesn't cross the pnpm symlink farm (pnpm hangs each # package off a `.pnpm/` symlink, and `git check-ignore` itself refuses with "beyond a symbolic link" on # those paths). # Each entry is an unanchored regex over the path, so it is anchored to a whole directory name: a bare "data" also # dropped every file under the `*-data1` modules. exclude_path = ['(^|/)node_modules/', '(^|/)target/', '\.local\.[^/]+$', '^(\./)?data/', '^(\./)?generated/'] # URL patterns (regex) to skip, none of which are real links. # doc/test placeholder hosts (e.g. http://host:8080/) and `{...}` format-string templates # (.../{repo}/{git_ref}/...). # # GitHub Actions run/job URLs are also skipped: workaround comments pin them as evidence of WHERE a failure # was observed, fully expecting them to age out (GHA log retention is 3 months), and github.com serves those # pages inconsistently to anonymous clients even while they exist -- the check lane got # [404] https://github.com/edge-toolkit/core/actions/runs/28698136445/job/85111320237 (at 447:1) # from AGENTS.md's attestation-flake note on commit # https://github.com/edge-toolkit/core/commit/e401831a98197068f978ef760134caff882e2615 while the same URL # returned 200 to a local curl minutes later. Checking them is all noise, no signal. # # The `file:` entry is the container-internal storage mount the compose generator emits as a `STORAGE_URL` # value; it names a path inside the ws-server image, so it is never resolvable from the host doing the check. # Scoped to that exact URL rather than the whole `file:` scheme, because relative links in markdown resolve to # `file:` URIs too and those are real links worth checking. # # The README's Open Hub badge and project page are skipped because openhub.net puts every request behind a # Cloudflare bot check, answering anything but an interactive browser with `HTTP 403 Forbidden` -- the page # exists, but no non-browser client can ever see it. Scoped to this project's pages, not the whole host. exclude = [ '\{|%7B', '^file:///app/storage$', '^https://github\.com/[^/]+/[^/]+/actions/runs/', # The base the Pico 2 W firmware appends a package path to; alone it names no package, and jsDelivr answers 400. '^https://cdn\.jsdelivr\.net/npm/?$', # Kubernetes API address; reachable only from inside the cluster. '^https://kubernetes\.default\.svc/?$', '^https://openhub\.net/p/edge-toolkit(/|$)', '^https?://host[:/]', ]