[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M78FKA02N6VRGZCSAR7C4ZSS":3,"skill-same:01M78FKA02N6VRGZCSAR7C4ZSS":47},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":27,"repositoryHtmlUrl":28,"repositoryHomepageUrl":6,"ownerHtmlUrl":29,"ownerBlogUrl":30,"ownerDescription":31,"updatedAt":32,"install":33,"resources":46},"01M78FKA02N6VRGZCSAR7C4ZSS","solid-auth",null,"Use when building or hardening Solid server token verification, DPoP proofs, WebID resolution, replay protection, SSRF guards with DNS pinning, or Keycloak realm preflight. Covers issuer-agnostic oauth4webapi verification and bidirectional WebID\u002Fissuer checks; client login is covered by solid-reactive-authentication and solid-auth-core.","jeswr\u002Fsolid-sdk","jeswr","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F63333554?v=4","01M78F8AE112CQ0KHFC3R0Z8SJ","Jesse Wright","01M78F8AERJX4YWDA1S4BMF74V",2,"MIT","https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Fskills\u002Fsolid-auth\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: solid-auth\ndescription: Use when building or hardening Solid server token verification, DPoP proofs, WebID resolution, replay protection, SSRF guards with DNS pinning, or Keycloak realm preflight. Covers issuer-agnostic oauth4webapi verification and bidirectional WebID\u002Fissuer checks; client login is covered by solid-reactive-authentication and solid-auth-core.\n---\n\n## Quick reference\n\nVerify tokens and DPoP proofs with `oauth4webapi`; allowlist issuers before discovery and asymmetric algorithms only.\nFail closed on replay-store outages, untrusted WebID\u002Fissuer assertions, unsafe DNS answers, or invalid claims.\nPin validated DNS addresses, bound profile fetches, and refuse remote JSON-LD contexts.\n\n\u003C!-- AUTHORED-BY Codex GPT-6.1-Sol -->\n\nImplementation paths, ADR numbers, server-specific defaults, and commands below refer to\nthe original `prod-solid-server` implementation, outside this SDK checkout. They document\nthat server's patterns, rather than files or dependencies to install in this workspace.\n\n# Solid auth (Solid-OIDC \u002F DPoP \u002F WebID verification)\n\nSecurity-critical patterns for verifying Solid-OIDC access tokens on a resource server. Grounded in\n`src\u002Fauth\u002F{verifier,webidResolver,replayStore,replay,realmPreflight,types}.ts` and ADR-0004. The core\nis **`oauth4webapi`** (panva) + **`undici`** (DNS-pinned fetch) + **`ioredis`\u002F`lru-cache`** (replay +\nprofile caches). Use **Context7** for exact `oauth4webapi` APIs. 🔒 = security-critical; get these right.\n\n## When to use\n\nVerifying Solid-OIDC \u002F DPoP-bound access tokens; resolving\u002Fvalidating WebID profiles; implementing\nreplay protection; defending against issuer compromise or SSRF; wiring trusted issuers \u002F Keycloak.\n\n\u003C!-- AUTHORED-BY Codex GPT-6 -->\n\n## 🔒 Verify with oauth4webapi — never hand-roll\n\n`oauth4webapi.validateJwtAccessToken(as, request, audience, opts)` does the RFC 6750\u002F9068\u002F9449 core:\nJWS signature against the issuer's JWKS, `typ=at+jwt`, required claims, **DPoP proof binding**\n(`typ=dpop+jwt`, `htm`\u002F`htu`\u002F`ath`\u002F`cnf.jkt`), and `iat` freshness. **Never hand-roll signature or\nproof validation.** Pre-check the **trusted-issuer allowlist against the *unverified* `iss`** *before*\ndiscovery — otherwise an attacker-controlled `iss` makes you fetch an SSRF discovery URL.\n\n```ts\nif (!trustedIssuers.has(unverifiedIss)) throw challenge(\"invalid_token\", \"Untrusted issuer.\");\nconst claims = await oauth.validateJwtAccessToken(as, request, audience, {\n  requireDPoP: dpop, signingAlgorithms: SIGNING_ALGS, [oauth.clockTolerance]: 5,\n});\n```\n\nThe result is the claims object itself ([return contract](https:\u002F\u002Fgithub.com\u002Fpanva\u002Foauth4webapi\u002Fblob\u002Fmain\u002Fdocs\u002Ffunctions\u002FvalidateJwtAccessToken.md));\n`clockTolerance` is a symbol-keyed option ([options](https:\u002F\u002Fgithub.com\u002Fpanva\u002Foauth4webapi\u002Fblob\u002Fmain\u002Fdocs\u002Finterfaces\u002FValidateJWTAccessTokenOptions.md)).\n\n## 🔒 Asymmetric algorithms only\n\nAllowlist `ES256\u002F384\u002F512`, `PS256\u002F384\u002F512`, `RS256\u002F384\u002F512`. **Reject `HS*` (symmetric → token\nforgery) and `none` (no validation).** Pass the list as `signingAlgorithms`, and advertise it in the\n`WWW-Authenticate` challenge (`algs=\"…\"`, RFC 9449 §5.1).\n\n## 🔒 DPoP `jti` replay cache — fail-closed, checked early\n\nUse the cryptographically verified **DPoP proof's** `jti` and `iat` (not the access token's claims\nor an unverified JWT decode). Reject `iat` more than `clockTolerance` in the future. Retain each\n`jti` until `iat + maxAge + clockTolerance`, the last instant that proof can be accepted. Compute\nthe remaining TTL from that deadline; a fixed `maxAge + clockTolerance` from first receipt can\nexpire too early for a future-dated proof within tolerance. Use atomic `SET key 1 NX EX ttl`\n(Redis) or an in-process LRU that retains entries through the same deadline.\n**Check replay BEFORE the expensive bidirectional WebID fetch.** Production\n**fail-closed**: Redis down → `503` + `WWW-Authenticate`. Dev escape valve (`PSS_REDIS_FAIL_OPEN_DEV=true`,\nrefused in `NODE_ENV=production`) falls back to in-process with a rate-limited warn.\n\n```ts\n\u002F\u002F jti and iat come from the verified DPoP proof; keep these limits aligned with verification.\nconst maxAge = 300; \u002F\u002F seconds\nconst clockTolerance = 5; \u002F\u002F seconds\nconst now = Date.now() \u002F 1000;\nconst acceptUntil = iat + maxAge + clockTolerance;\nif (iat > now + clockTolerance || now > acceptUntil) {\n  throw challenge(\"invalid_token\", \"DPoP proof outside acceptance window.\");\n}\n\u002F\u002F Use whole Redis EX seconds strictly past the inclusive acceptance boundary.\nconst ttl = Math.floor(acceptUntil - now) + 1;\nif (await replay.mark(jti, ttl) === \"replay\") throw challenge(\"invalid_token\", \"DPoP proof replayed.\");\n```\n\n## 🔒 Bidirectional WebID↔issuer check (defence vs issuer compromise)\n\nAfter the token passes, dereference the WebID profile and require `\u003Cwebid> solid:oidcIssuer \u003Cclaims.iss>`.\nA compromised\u002Frogue issuer can mint a token for *any* `webid` claim; this triple is the profile owner's\ncounter-assertion of which issuer they trust. Mode-aware: `strict` (default) → 401 on mismatch; `warn` →\nlog+accept; `off` → skip. **Auto-downgrades to `warn` only when the mode is unset AND all trusted issuers\nare loopback HTTP** (dev\u002FCI). Surface a **constant** client error; log the real reason internally.\n\n## 🔒 SSRF guard + DNS pinning on profile fetch\n\nWebID profiles are attacker-influenced URLs. Resolve the host once, classify **every** A\u002FAAAA record,\nand **refuse if any** is non-public: loopback, RFC-1918, CGNAT `100.64\u002F10`, link-local, IPv4-mapped\n`::ffff:*`, IPv6 ULA `fc00::\u002F7`, multicast, plus `6to4 2002::\u002F16` and NAT64 `64:ff9b::\u002F96` with their\n**embedded-IPv4** re-checked. **Pin the first validated IP into the fetch** so guard and socket see the\nsame address (closes DNS-rebinding TOCTOU), and **re-run the guard on every redirect `Location`**.\n\n```ts\nconst pinned = await assertNotSsrf(url);                 \u002F\u002F throws on any non-public record\nconst res = await fetcher(url, {\n  dispatcher: new Agent({ connect: { lookup: (_h,_o,cb) => cb(null, [pinned]) } }),\n  redirect: \"manual\",                                    \u002F\u002F re-guard each hop\n});\n```\n\n## 🔒 Bound the fetch; no remote JSON-LD context\n\nOne `AbortController` spans the whole resolution (fetch + redirects + body read); reject\n`Content-Length > maxBytes` (256 KiB) up front and abort on stream overflow. Parse profile JSON-LD with a\n**bundled-context-only loader** (solid terms\u002Foidc-context, foaf, pim\u002Fspace) — any remote `@context` →\nreject (same rule as [solid-rdf](..\u002Fsolid-rdf\u002FSKILL.md)'s SSRF-safe loader).\n\n## 🔒 Claim hardening\n\n- **WebID claim**: present, non-empty, valid URL, **`https:` only**, **no userinfo** (`user:pass@`) — a\n  non-https or userinfo WebID leaks credentials on fetch.\n- **Audience** (RFC 9068 §3): require `aud` includes this RS's URL.\n- **Authorized party**: when configured, require `azp` (or `client_id`) ∈ allowlist — blocks a token the\n  issuer minted for a *different* client\u002FRS (audience confusion).\n\n## 🔒 Realm preflight gate (boot-time, fail-closed)\n\nAt boot, for every trusted **Keycloak** realm, fetch `…\u002Fadmin\u002Frealms\u002F{realm}\u002Fusers\u002Fprofile` and assert the\n`webid` attribute is **admin-edit-only** (`permissions.edit: [\"admin\"]`). If a user can edit their own\n`webid`, any account mints tokens for any WebID — the identity model collapses. Refuse to boot otherwise.\nNon-Keycloak issuers fail-closed unless explicitly allow-listed (typo guard). (Boot-only; add a periodic\nre-check for live drift.)\n\n## Performance: two-tier WebID cache (not security, but in the brief)\n\nL1 in-process LRU (sync, no Redis hop on hits) → L2 Redis → on full miss, a per-WebID `SET NX EX 8s`\nlock: the winner fetches + populates + signals; losers wait (bounded ~6s) then re-read L2, else fetch\nthemselves — bounding amplification to at-worst-once-per-replica. Negative cache (~30s) memoises failures.\nSee [solid-rdf](..\u002Fsolid-rdf\u002FSKILL.md) for the RDF parsing and the bundled-context loader these reuse.\n\n## Gotchas\n\n- [ ] Trusted-issuer allowlist checked against the **unverified** `iss` *before* discovery (SSRF).\n- [ ] Asymmetric algs only; never `HS*`\u002F`none`.\n- [ ] Reject future `iat` beyond tolerance; retain `jti` through `iat + maxAge + clockTolerance`; check replay **before** the bidirectional fetch.\n- [ ] SSRF guard refuses if **any** DNS record is non-public; fetch **pinned**; redirects re-guarded.\n- [ ] One AbortController for the whole resolution; byte cap enforced; remote `@context` refused.\n- [ ] WebID is `https:` with no userinfo; `aud` + `azp` enforced.\n- [ ] Realm preflight is fail-closed at boot; non-Keycloak issuers opt-in only.\n- [ ] Client-facing auth errors are **constant** (no reconnaissance oracle); real reason logged internally.\n- [ ] Prod fail-closed on replay-store outage (503); `FAIL_OPEN_DEV` refused in production.\n\n## See also\n\n- Code: `src\u002Fauth\u002F{verifier,webidResolver,replayStore,realmPreflight}.ts`; ADR-0004 (trust model),\n  ADR-0012 §4.1\u002F§4.3 (shared replay + WebID L2). Related skills: [solid-rdf](..\u002Fsolid-rdf\u002FSKILL.md), [solid-wac](..\u002Fsolid-wac\u002FSKILL.md),\n  [solid-conformance](..\u002Fsolid-conformance\u002FSKILL.md).\n- Specs \u002F libs (Context7): `oauth4webapi`, `undici`, RFC 6750 \u002F 9068 \u002F 9449, Solid-OIDC (W3C),\n  RFC 1918 \u002F 6598 \u002F 3056 \u002F 6052 (IP ranges), Keycloak declarative user profile.\n","## Quick reference\n\nVerify tokens and DPoP proofs with `oauth4webapi`; allowlist issuers before discovery and asymmetric algorithms only.\nFail closed on replay-store outages, untrusted WebID\u002Fissuer assertions, unsafe DNS answers, or invalid claims.\nPin validated DNS addresses, bound profile fetches, and refuse remote JSON-LD contexts.\n\n\u003C!-- AUTHORED-BY Codex GPT-6.1-Sol -->\n\nImplementation paths, ADR numbers, server-specific defaults, and commands below refer to\nthe original `prod-solid-server` implementation, outside this SDK checkout. They document\nthat server's patterns, rather than files or dependencies to install in this workspace.\n\n# Solid auth (Solid-OIDC \u002F DPoP \u002F WebID verification)\n\nSecurity-critical patterns for verifying Solid-OIDC access tokens on a resource server. Grounded in\n`src\u002Fauth\u002F{verifier,webidResolver,replayStore,replay,realmPreflight,types}.ts` and ADR-0004. The core\nis **`oauth4webapi`** (panva) + **`undici`** (DNS-pinned fetch) + **`ioredis`\u002F`lru-cache`** (replay +\nprofile caches). Use **Context7** for exact `oauth4webapi` APIs. 🔒 = security-critical; get these right.\n\n## When to use\n\nVerifying Solid-OIDC \u002F DPoP-bound access tokens; resolving\u002Fvalidating WebID profiles; implementing\nreplay protection; defending against issuer compromise or SSRF; wiring trusted issuers \u002F Keycloak.\n\n\u003C!-- AUTHORED-BY Codex GPT-6 -->\n\n## 🔒 Verify with oauth4webapi — never hand-roll\n\n`oauth4webapi.validateJwtAccessToken(as, request, audience, opts)` does the RFC 6750\u002F9068\u002F9449 core:\nJWS signature against the issuer's JWKS, `typ=at+jwt`, required claims, **DPoP proof binding**\n(`typ=dpop+jwt`, `htm`\u002F`htu`\u002F`ath`\u002F`cnf.jkt`), and `iat` freshness. **Never hand-roll signature or\nproof validation.** Pre-check the **trusted-issuer allowlist against the *unverified* `iss`** *before*\ndiscovery — otherwise an attacker-controlled `iss` makes you fetch an SSRF discovery URL.\n\n```ts\nif (!trustedIssuers.has(unverifiedIss)) throw challenge(\"invalid_token\", \"Untrusted issuer.\");\nconst claims = await oauth.validateJwtAccessToken(as, request, audience, {\n  requireDPoP: dpop, signingAlgorithms: SIGNING_ALGS, [oauth.clockTolerance]: 5,\n});\n```\n\nThe result is the claims object itself ([return contract](https:\u002F\u002Fgithub.com\u002Fpanva\u002Foauth4webapi\u002Fblob\u002Fmain\u002Fdocs\u002Ffunctions\u002FvalidateJwtAccessToken.md));\n`clockTolerance` is a symbol-keyed option ([options](https:\u002F\u002Fgithub.com\u002Fpanva\u002Foauth4webapi\u002Fblob\u002Fmain\u002Fdocs\u002Finterfaces\u002FValidateJWTAccessTokenOptions.md)).\n\n## 🔒 Asymmetric algorithms only\n\nAllowlist `ES256\u002F384\u002F512`, `PS256\u002F384\u002F512`, `RS256\u002F384\u002F512`. **Reject `HS*` (symmetric → token\nforgery) and `none` (no validation).** Pass the list as `signingAlgorithms`, and advertise it in the\n`WWW-Authenticate` challenge (`algs=\"…\"`, RFC 9449 §5.1).\n\n## 🔒 DPoP `jti` replay cache — fail-closed, checked early\n\nUse the cryptographically verified **DPoP proof's** `jti` and `iat` (not the access token's claims\nor an unverified JWT decode). Reject `iat` more than `clockTolerance` in the future. Retain each\n`jti` until `iat + maxAge + clockTolerance`, the last instant that proof can be accepted. Compute\nthe remaining TTL from that deadline; a fixed `maxAge + clockTolerance` from first receipt can\nexpire too early for a future-dated proof within tolerance. Use atomic `SET key 1 NX EX ttl`\n(Redis) or an in-process LRU that retains entries through the same deadline.\n**Check replay BEFORE the expensive bidirectional WebID fetch.** Production\n**fail-closed**: Redis down → `503` + `WWW-Authenticate`. Dev escape valve (`PSS_REDIS_FAIL_OPEN_DEV=true`,\nrefused in `NODE_ENV=production`) falls back to in-process with a rate-limited warn.\n\n```ts\n\u002F\u002F jti and iat come from the verified DPoP proof; keep these limits aligned with verification.\nconst maxAge = 300; \u002F\u002F seconds\nconst clockTolerance = 5; \u002F\u002F seconds\nconst now = Date.now() \u002F 1000;\nconst acceptUntil = iat + maxAge + clockTolerance;\nif (iat > now + clockTolerance || now > acceptUntil) {\n  throw challenge(\"invalid_token\", \"DPoP proof outside acceptance window.\");\n}\n\u002F\u002F Use whole Redis EX seconds strictly past the inclusive acceptance boundary.\nconst ttl = Math.floor(acceptUntil - now) + 1;\nif (await replay.mark(jti, ttl) === \"replay\") throw challenge(\"invalid_token\", \"DPoP proof replayed.\");\n```\n\n## 🔒 Bidirectional WebID↔issuer check (defence vs issuer compromise)\n\nAfter the token passes, dereference the WebID profile and require `\u003Cwebid> solid:oidcIssuer \u003Cclaims.iss>`.\nA compromised\u002Frogue issuer can mint a token for *any* `webid` claim; this triple is the profile owner's\ncounter-assertion of which issuer they trust. Mode-aware: `strict` (default) → 401 on mismatch; `warn` →\nlog+accept; `off` → skip. **Auto-downgrades to `warn` only when the mode is unset AND all trusted issuers\nare loopback HTTP** (dev\u002FCI). Surface a **constant** client error; log the real reason internally.\n\n## 🔒 SSRF guard + DNS pinning on profile fetch\n\nWebID profiles are attacker-influenced URLs. Resolve the host once, classify **every** A\u002FAAAA record,\nand **refuse if any** is non-public: loopback, RFC-1918, CGNAT `100.64\u002F10`, link-local, IPv4-mapped\n`::ffff:*`, IPv6 ULA `fc00::\u002F7`, multicast, plus `6to4 2002::\u002F16` and NAT64 `64:ff9b::\u002F96` with their\n**embedded-IPv4** re-checked. **Pin the first validated IP into the fetch** so guard and socket see the\nsame address (closes DNS-rebinding TOCTOU), and **re-run the guard on every redirect `Location`**.\n\n```ts\nconst pinned = await assertNotSsrf(url);                 \u002F\u002F throws on any non-public record\nconst res = await fetcher(url, {\n  dispatcher: new Agent({ connect: { lookup: (_h,_o,cb) => cb(null, [pinned]) } }),\n  redirect: \"manual\",                                    \u002F\u002F re-guard each hop\n});\n```\n\n## 🔒 Bound the fetch; no remote JSON-LD context\n\nOne `AbortController` spans the whole resolution (fetch + redirects + body read); reject\n`Content-Length > maxBytes` (256 KiB) up front and abort on stream overflow. Parse profile JSON-LD with a\n**bundled-context-only loader** (solid terms\u002Foidc-context, foaf, pim\u002Fspace) — any remote `@context` →\nreject (same rule as [solid-rdf](..\u002Fsolid-rdf\u002FSKILL.md)'s SSRF-safe loader).\n\n## 🔒 Claim hardening\n\n- **WebID claim**: present, non-empty, valid URL, **`https:` only**, **no userinfo** (`user:pass@`) — a\n  non-https or userinfo WebID leaks credentials on fetch.\n- **Audience** (RFC 9068 §3): require `aud` includes this RS's URL.\n- **Authorized party**: when configured, require `azp` (or `client_id`) ∈ allowlist — blocks a token the\n  issuer minted for a *different* client\u002FRS (audience confusion).\n\n## 🔒 Realm preflight gate (boot-time, fail-closed)\n\nAt boot, for every trusted **Keycloak** realm, fetch `…\u002Fadmin\u002Frealms\u002F{realm}\u002Fusers\u002Fprofile` and assert the\n`webid` attribute is **admin-edit-only** (`permissions.edit: [\"admin\"]`). If a user can edit their own\n`webid`, any account mints tokens for any WebID — the identity model collapses. Refuse to boot otherwise.\nNon-Keycloak issuers fail-closed unless explicitly allow-listed (typo guard). (Boot-only; add a periodic\nre-check for live drift.)\n\n## Performance: two-tier WebID cache (not security, but in the brief)\n\nL1 in-process LRU (sync, no Redis hop on hits) → L2 Redis → on full miss, a per-WebID `SET NX EX 8s`\nlock: the winner fetches + populates + signals; losers wait (bounded ~6s) then re-read L2, else fetch\nthemselves — bounding amplification to at-worst-once-per-replica. Negative cache (~30s) memoises failures.\nSee [solid-rdf](..\u002Fsolid-rdf\u002FSKILL.md) for the RDF parsing and the bundled-context loader these reuse.\n\n## Gotchas\n\n- [ ] Trusted-issuer allowlist checked against the **unverified** `iss` *before* discovery (SSRF).\n- [ ] Asymmetric algs only; never `HS*`\u002F`none`.\n- [ ] Reject future `iat` beyond tolerance; retain `jti` through `iat + maxAge + clockTolerance`; check replay **before** the bidirectional fetch.\n- [ ] SSRF guard refuses if **any** DNS record is non-public; fetch **pinned**; redirects re-guarded.\n- [ ] One AbortController for the whole resolution; byte cap enforced; remote `@context` refused.\n- [ ] WebID is `https:` with no userinfo; `aud` + `azp` enforced.\n- [ ] Realm preflight is fail-closed at boot; non-Keycloak issuers opt-in only.\n- [ ] Client-facing auth errors are **constant** (no reconnaissance oracle); real reason logged internally.\n- [ ] Prod fail-closed on replay-store outage (503); `FAIL_OPEN_DEV` refused in production.\n\n## See also\n\n- Code: `src\u002Fauth\u002F{verifier,webidResolver,replayStore,realmPreflight}.ts`; ADR-0004 (trust model),\n  ADR-0012 §4.1\u002F§4.3 (shared replay + WebID L2). Related skills: [solid-rdf](..\u002Fsolid-rdf\u002FSKILL.md), [solid-wac](..\u002Fsolid-wac\u002FSKILL.md),\n  [solid-conformance](..\u002Fsolid-conformance\u002FSKILL.md).\n- Specs \u002F libs (Context7): `oauth4webapi`, `undici`, RFC 6750 \u002F 9068 \u002F 9449, Solid-OIDC (W3C),\n  RFC 1918 \u002F 6598 \u002F 3056 \u002F 6052 (IP ranges), Keycloak declarative user profile.\n",{"name":5,"description":7},"main","5482abfe2666cc12f835fe360e8fad1f1e1dea8c","skills\u002Fsolid-auth",0,"https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk","https:\u002F\u002Fgithub.com\u002Fjeswr","jeswr.org\u002F#me","@solid Lead @theodi | PhD Candidate @OxfordHCC\r\n\r\n","2026-11-15 05:20:24",{"source":8,"skillName":5,"commands":34,"prompt":44,"zipAvailable":45},[35,38,41],{"tool":36,"command":37},"npx","npx skills add jeswr\u002Fsolid-sdk --skill solid-auth",{"tool":39,"command":40},"bunx","bunx skills add jeswr\u002Fsolid-sdk --skill solid-auth",{"tool":42,"command":43},"pnpm","pnpm dlx skills add jeswr\u002Fsolid-sdk --skill solid-auth","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Fskills\u002Fsolid-auth\u002FSKILL.md",true,[],[48,55,62,69,76,83],{"skillNo":49,"name":50,"nameZh":6,"description":51,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":52,"categories":53,"keywords":54,"publishedAt":19},"01M78FNRF6EV6SKPEYRZ6JTTQ3","storage-conformance","Work on @jeswr\u002Fstorage-conformance, the shared storage conformance suite package in the Solid SDK workspace.","https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Ftools\u002Fstorage-conformance\u002FSKILL.md",[],[],{"skillNo":56,"name":57,"nameZh":6,"description":58,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":59,"categories":60,"keywords":61,"publishedAt":19},"01M78FNMS7HQGD9EE43WAFEW6A","fetch-rdf-engine-tests","Maintain fetch-rdf's real Node WASM parser integration tests without introducing a reverse engine dependency in fetch-rdf.","https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Ftools\u002Ffetch-rdf-engine-tests\u002FSKILL.md",[],[],{"skillNo":63,"name":64,"nameZh":6,"description":65,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":68,"publishedAt":19},"01M78FNK5M6ASAR3909JT6BZV1","alpha","Use when checking fixtures, including folded YAML descriptions.","https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Ftest\u002Ffixtures\u002Fcheck-skills\u002Fvalid\u002Fskills\u002Falpha\u002FSKILL.md",[],[],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":19},"01M78FNGATR8F4N09HWSFVWBAF","widget","Use when testing package skills: YAML quoting works.","https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Ftest\u002Ffixtures\u002Fcheck-skills\u002Fvalid\u002Fpackages\u002Fwidget\u002FSKILL.md",[],[],{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":82,"publishedAt":19},"01M78FN9M05GCHYTK848SSMN9R","web-seo","Use when improving technical and entity SEO for a modern personal, portfolio, documentation, or marketing site, including crawlable HTML, canonical metadata, sitemap and robots, Person\u002FProfilePage JSON-LD, Core Web Vitals, and honest ranking expectations.","https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Fskills\u002Fweb-seo\u002FSKILL.md",[],[],{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":19},"01M78FN5ANETMSPMEFACSJ7JMC","solid-wac","Use when implementing or auditing Solid server WAC authorization, ACL creation and inheritance, auxiliary-resource escalation, method-to-mode mapping, 401 versus 403 challenges, WAC-Allow, or defensive typed ACL parsing with @solid\u002Fobject and @rdfjs\u002Fwrapper.","https:\u002F\u002Fgithub.com\u002Fjeswr\u002Fsolid-sdk\u002Fblob\u002Fmain\u002Fskills\u002Fsolid-wac\u002FSKILL.md",[],[]]