L2: LWS Storage Description + per-resource Linkset (discovery slice, --lws)#2
Merged
Conversation
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Register GET /.well-known/lws-storage gated on lwsEnabled - Returns generateStorageDescription(root, services) as application/lws+json - root = proto://hostname/ (subdomain-safe); services includes StorageDescription self-entry; conditionally adds NotificationService when notificationsEnabled - 405 on PUT/POST/PATCH/DELETE (reuses existing methodNotAllowed helper) - Route not registered at all when --lws is off (404 from wildcard) - Add focused test (8 tests: 200+body+405s under --lws ON, 404 under --lws OFF) - Full suite: 1003 tests, 0 failures Co-Authored-By: Claude Opus 4.8 <[email protected]>
…--lws) Co-Authored-By: Claude Opus 4.8 <[email protected]>
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Add RDF_TYPES.LINKSET = 'application/linkset+json' to conneg.js - Add early-exit branch in selectContentType for LINKSET (independent of Turtle conneg flag, same pattern as LWS_JSON) - Add container-GET linkset branch in resource.js (after LWS_JSON branch) serving generateLinkset with isContainer:true under --lws + explicit Accept - Add file-GET linkset branch in resource.js (before conneg block) serving generateLinkset with isContainer:false under --lws + explicit Accept - Advertise <resourceUrl>; rel="linkset" in getAllHeaders (headers.js) alongside rel=storageDescription, both gated by lwsEnabled - Extend test/lws-conneg.test.js: LINKSET type + negotiability - Extend test/lws-storage-link.test.js: rel=linkset present/absent assertions Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Container branch: after existing conneg, apply LWS override when lwsEnabled so Accept: application/lws+json or application/linkset+json sets the matching Content-Type (mirrors GET's early-return for these types, resolves the TODO(lws-head-parity) marker). - File branch: after negotiateHeadFileContentType, override contentType to LINKSET when lwsEnabled + explicitly negotiated; mark negotiationConverted=true to suppress the on-disk Content-Length (generated linkset differs in size from the stored file). - HEAD already emitted storageDescription + linkset Link rels via the existing lwsEnabled path in getAllHeaders (Task 3); no change needed there. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…y + negative controls Three-suite e2e covering the L2 discovery slice: - --lws ON: storage description body shape (@context/type/service[]), storageDescription + linkset Link rels on GET, per-resource linkset via conneg (anchor/type/up), HEAD Link-rel parity, HEAD content-type parity for application/linkset+json (no body), LDP additivity control. - --lws OFF: /.well-known/lws-storage → 404; storageDescription and linkset rels absent on resource GET (negative controls). - --lws + --notifications: service[] contains NotificationService entry with serviceEndpoint ending /notification/api (Task 2 gap). Co-Authored-By: Claude Opus 4.8 <[email protected]>
…me parity) + guard/test tightening - src/server.js: replace `options.ssl ? 'https' : 'http'` with `request.protocol` in the GET /.well-known/lws-storage handler so the body's id and serviceEndpoints honor X-Forwarded-Proto (trustProxy:true), matching the scheme used by storageDescriptionUrl() and Updates-Via - src/lws/storage-description.js: add guard that throws a clear Error on relative/non-URL args before new URL() can throw a raw TypeError - test/lws-storage-description-route.test.js: add proxy-scheme-parity test that sends X-Forwarded-Proto: https and asserts id + StorageDescription serviceEndpoint both start with https:// - test/lws-storage-link.test.js: add test asserting the guard throws with /requires an absolute URL/ on '/alice/note.ttl' - test/lws-conneg.test.js: add assertion that selectContentType with connegEnabled=true also returns 'application/linkset+json' (fires before the connegEnabled guard) Co-Authored-By: Claude Opus 4.8 <[email protected]>
charlesvardeman
added a commit
that referenced
this pull request
Jul 3, 2026
…rors, sanitize Review fixes #2,JavaScriptSolidServer#3,JavaScriptSolidServer#5,JavaScriptSolidServer#6,JavaScriptSolidServer#8,JavaScriptSolidServer#12: - JavaScriptSolidServer#3 readAcl: detect container-ness from storage and resolve <dir>/.acl (not <dir>.acl) for a slashless container path, and run the Control check against the normalized target — was showing an empty ACL for a governed container. - JavaScriptSolidServer#5/JavaScriptSolidServer#6/JavaScriptSolidServer#12 new src/mcp/read.js#readBounded: one byte-range bounded read shared by lws://resource and describe_resource. Bounds memory (no full read of a huge object), and surfaces an explicit truncation marker so a clipped body isn't taken as whole. Body content-type label now uses getContentType. - JavaScriptSolidServer#8 readSkillResource: only a genuinely-missing skill is not-found; a read/ parse failure surfaces as INTERNAL_ERROR instead of being masked. - #2 sanitizeTypes: client-controlled declared types + describedby shape IRIs are stripped of hidden/bidi chars before entering linkset/describe responses; captureDeclaredTypes now validates the MCP `types` path to absolute URIs (the HTTP Link path already did) so no free-text value is persisted. - JavaScriptSolidServer#12 wac.parentPath delegates to utils/url.getParentContainer. Co-Authored-By: Claude Opus 4.8 <[email protected]>
charlesvardeman
added a commit
that referenced
this pull request
Jul 3, 2026
Correctness/security: transactional put_typed_resource .meta (#1), type/describedby + federated-content sanitize (#2/JavaScriptSolidServer#7), container ACL-path resolution (JavaScriptSolidServer#3), malformed-percent invalid-params (JavaScriptSolidServer#4), bounded read + truncation signal (JavaScriptSolidServer#5/JavaScriptSolidServer#6), skill read-error not masked (JavaScriptSolidServer#8), resource-error teaching content (JavaScriptSolidServer#9). Maintainability: single surface registry (JavaScriptSolidServer#11), helper reuse (JavaScriptSolidServer#12), restored live-gate coverage (JavaScriptSolidServer#10, lws-pod repo). All fork changes TDD'd in test/mcp-v2-review-fixes.test.js; mcp/lws/wac/acl/ storage/container/handler suites green per-file.
charlesvardeman
added a commit
that referenced
this pull request
Jul 13, 2026
…he write choke point (review #2, JavaScriptSolidServer#10) - writeTypeConsistency runs inside applyLwsWrite: HTTP PUT/POST and all three MCP write tools share ONE gate; the MCP bypass is closed - application/json gates as JSON-LD (pipeline parity) - non-RDF body at an RDF-extension name is refused (the SHACL-skip lie) - callers handle the new problem return; HTTP local gate calls deleted Co-Authored-By: Claude Opus 4.8 <[email protected]>
charlesvardeman
added a commit
that referenced
this pull request
Jul 13, 2026
…message (task-6 review #2) - the --lws-off 415 base string returns to the pre-round wording ("...text/n3 (N3 Patch) or application/sparql-update (SPARQL Update)"); the merge-patch clause is appended only under request.lwsEnabled - new test pins the exact --lws-off 415 body with deepEqual against a { conneg: true } (no lws) server Co-Authored-By: Claude Opus 4.8 <[email protected]>
charlesvardeman
added a commit
that referenced
this pull request
Jul 13, 2026
…ag/304 conformance, PATCH MUSTs, MCP sanitize + SSRF consolidation (review 2026-07-12 B1-B12 + P1-P3) Closes the 12 (B) findings + 3 (C) pre-existing conformance violations from the 2026-07-12 post-drain code review, TDD per finding, each task spec+quality reviewed: - #2/JavaScriptSolidServer#10 write-consistency gate moves into applyLwsWrite (the shared choke point); application/json gates as JSON-LD; non-RDF body at an RDF name refused - JavaScriptSolidServer#9 slug-less POST of RDF derives a server-side extension - JavaScriptSolidServer#6 verbatim NT/NQ parse through n3; JSON-LD is graph-capable - JavaScriptSolidServer#5 JSON-LD conversion arm gets a variant ETag (RFC 9110 §8.8.3) - JavaScriptSolidServer#4 pending RDF conversion defers the early 304; 406 carries no ETag (§13.2.2) - JavaScriptSolidServer#7/P1/P2 PATCH applies at the dataset level (no JSON-LD document detour); JSON Merge Patch (RFC 7386); bodied write without Content-Type → 400 - JavaScriptSolidServer#3/JavaScriptSolidServer#12/JavaScriptSolidServer#13 MCP sanitizes altr reps + type-search items; read_resource mimeType - JavaScriptSolidServer#8/JavaScriptSolidServer#14 per-hop redirect revalidation restores the 303 VoID rail; one SSRF table - P3 application/json is a first-class container + storage-description label Fork suite 1545/0/1 (the 1 skip + the isolated mcp-lws-read open-handle file both pre-existing). la3d/lws-review = 4824fe2..7165855, 18 commits. Co-Authored-By: Claude Opus 4.8 <[email protected]>
charlesvardeman
added a commit
that referenced
this pull request
Jul 14, 2026
…ntResolution capability - buildStorageDescription gains uriSpacePrefixes; the capability carries uriSpace: [<absolute prefix>...] (the void:uriSpace values) so a cold agent recognizes minted IRIs on its first storage-description read (probe #2) - prefixes only (container/suffix mapping stays internal; 303 is the resolver); threaded identically to HTTP + MCP; flag-off document byte-identical Co-Authored-By: Claude Opus 4.8 <[email protected]>
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.
What
Adds the L2 LWS storage-discovery layer over L1, all behind the existing
--lwsflag and strictly additive over LDP/Solid behavior. Delivers the two storage-side LWS discovery MUSTs:GET /.well-known/lws-storageserves{@context: ".../lws/v1", id, type: "Storage", service[]}asapplication/lws+json; advertised byLink: rel="https://www.w3.org/ns/lws#storageDescription"on all storage GET/HEAD responses.application/linkset+json(anchor/up/type/describedby) served via content negotiation from both container-GET and file-GET, advertised byLink: rel="linkset". Read-only (mutation deferred).Plus HEAD content-type parity for
lws+json/linkset+json(suppressing the stale on-diskContent-Length).Commits
generateStorageDescriptionpure fn → serve at/.well-known/lws-storagerel=storageDescriptionLink on all GET/HEAD (derived insidegetAllHeadersfrom resource origin)generateLinkset(RFC 9264) → serve via conneg +rel=linksetScope decisions (deliberate, see plan)
/.well-known/lws-configurationis out — it is RFC 8414 authorization-server metadata; JSS is an OAuth2 resource server with a direct bearer (no RFC 8693 token-exchange). It belongs to the auth/Keycloak track.capability/TypeIndex advertising deferred (L3/Tranche 2).Verification
--lws) LDP path provably unchanged (negative controls: lws-off → 404 + no rels; lws-on + default Accept → still LDP).options.sslvsrequest.protocol) — is fixed (8927ada) with anX-Forwarded-Protoregression test.Plan + grounding:
docs/superpowers/plans/2026-06-30-lws-L2-storage-discovery.md(in the lws-pod repo).🤖 Generated with Claude Code