feat: add API conformance tooling and @relayfile/core package#11
feat: add API conformance tooling and @relayfile/core package#11khaliqgant merged 9 commits intomainfrom
Conversation
Adds scripts/conformance.ts — a 23-test suite that verifies any relayfile-compatible server correctly implements the API contract. Both the Go server and the Cloudflare Workers server must pass this. Runnable against any server URL via RELAYFILE_BASE_URL env var. Covers: file CRUD, If-Match wildcard, conflict detection, cross-agent metadata visibility, semantic queries (property/relation/comment), ACL permission enforcement, WebSocket catch-up, writeback lifecycle, bulk ops, export, concurrent multi-agent writes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a skill that teaches agents how to work in a shared relayfile virtual filesystem — covering metadata semantics, conflict handling, discovery queries, ACL permissions, and collaboration patterns. Designed for agents on platforms like Daytona where multiple agents share the same workspace concurrently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
conformance.ts: correlation IDs now meet minLength:8 API spec requirement writeback.ts: remove status reset to pending after successful send (infinite loop) query.ts: fix spurious nextCursor when filters eliminate rows without hitting limit Co-Authored-By: My Senior Dev <dev@myseniordev.com>
Security fixes: - webhooks.ts: ACL enforcement hook, signature verification callback, strip permissions from webhook-provided semantics, pagination guard - files.ts: path traversal prevention via .. resolution in normalizePath - files.ts: conflict response no longer leaks file content preview - acl.ts: document default-open security model Reliability fixes: - semantics.ts: mergeSemantics now actually merges (was discarding existing) - semantics.ts: size limits on arrays (MAX_ARRAY_ENTRIES=100) - writeback.ts: correlationId parameter now used (was voided) - writeback.ts: getPendingWritebacks filters by status=pending - writeback.ts: dispatchWriteback sets status to dispatched after send - export.ts: null guard on Response.body (was non-null assertion) - storage.ts: document concurrency contract for StorageAdapter Code quality: - utils.ts: extract normalizePath with .. resolution (was duplicated 6x) - files.ts: deduplicate normalizeSemantics (import from semantics.ts) Testing: - conformance.ts: health check captures and reports errors (was empty catch) - conformance.ts: writeback test asserts items exist (was silently skipping) - conformance.ts: typed interfaces replace pervasive any casts - conformance.ts: ACL gap now tracked as test failure - conformance.ts: JWT query string security concern documented Co-Authored-By: My Senior Dev <dev@myseniordev.com>
| function normalizePath(path: string): string { | ||
| const trimmed = path.trim(); | ||
| if (!trimmed) { | ||
| return "/"; | ||
| } | ||
| const prefixed = trimmed.startsWith("/") ? trimmed : `/${trimmed}`; | ||
| return prefixed.length > 1 ? prefixed.replace(/\/+$/, "") : "/"; | ||
| } |
There was a problem hiding this comment.
🔴 ACL module uses local normalizePath without .. resolution while utils.ts has the secure version
The acl.ts module has a local normalizePath (line 170-177) that does NOT resolve . or .. segments, while utils.ts was specifically created to "resolve . and .. segments to prevent path traversal attacks" and is imported by files.ts for file writes. Since resolveFilePermissions is a public API exported from @relayfile/core (packages/core/src/index.ts:24), any external caller passing a path with .. segments (e.g., /finance/../secrets/data) would get incorrect ancestor directory resolution — the ACL walker would look for markers at /finance/.. instead of resolving to /. The same incomplete migration affects query.ts:123, tree.ts:135, operations.ts:116, and writeback.ts:158.
Prompt for agents
Replace the local normalizePath in packages/core/src/acl.ts (lines 170-177) with an import of the shared normalizePath from packages/core/src/utils.ts. The shared version resolves . and .. segments to prevent path traversal.
Specifically:
1. In packages/core/src/acl.ts, add: import { normalizePath } from "./utils.js";
2. Remove the local normalizePath function (lines 170-177)
3. Do the same for packages/core/src/query.ts (lines 123-130), packages/core/src/tree.ts (lines 135-142), packages/core/src/operations.ts (lines 116-123), and packages/core/src/writeback.ts (lines 158-165)
All five modules should import from ./utils.js instead of having their own copy that lacks .. resolution.
Was this helpful? React with 👍 or 👎 to provide feedback.
contract.yml: <!-- devin-review-comment {"id": "BUG_pr-review-job-ca1d91a9d6c748
webhooks.ts: <!-- devin-review-comment {"id": "BUG_pr-review-job-ccdef9cc24a84af
webhooks.ts: <!-- devin-review-comment {"id": "BUG_pr-review-job-ccdef9cc24a84af
acl.ts: <!-- devin-review-comment {"id": "BUG_pr-review-job-ccdef9cc24a84af5abb6
Co-Authored-By: My Senior Dev <dev@myseniordev.com>
5b2c1db to
35a042c
Compare
Summary
scripts/conformance.ts): 23-test suite that verifies any relayfile-compatible server implements the API contract correctly. Runnable against any server viaRELAYFILE_BASE_URL..claude/skills/relayfile-workspace/SKILL.md): guidance for agents collaborating in a shared relayfile virtual filesystem.@relayfile/corepackage (packages/core/): shared pure business logic extracted fromrelayfile-cloud'sworkspace.tsbehind aStorageAdapterinterface.@relayfile/corecontainsWhy this package exists
workspace.tsbehind theStorageAdapterinterface.relayfile-cloudwhile centralizing the reusable domain logic.relayfile-cloudrefactor will import this package frompackages/coreinstead of keeping those implementations inline.Test plan
npx tsx scripts/conformance.ts --cipasses 23/23 against Go servernpm run buildpasses inpackages/core