Skip to content

Commit 324b199

Browse files
khaliqgantclaude
andcommitted
fix: ACL seeding via bulk write endpoint, always seed dotfile ACLs
- seedAclEntries now uses POST /fs/bulk instead of PUT /fs/file (bulk write doesn't require If-Match for new files) - Always seed compiled ACLs in dotfile mode (was only seeding for relay.yaml) - ACL markers (.relayfile.acl) now created in relayfile workspace Note: relayfile Go server doesn't yet enforce .relayfile.acl markers — ACL eval logic exists in @relayfile/core (TypeScript) but isn't wired into Go HTTP handlers. Separate PR needed in relayfile repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1ed391e commit 324b199

2 files changed

Lines changed: 27 additions & 24 deletions

File tree

packages/core/src/acl.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,34 @@ export async function seedAclEntries(
88
): Promise<void> {
99
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
1010

11-
for (const [dirPath, rules] of Object.entries(acl)) {
12-
const aclPath = dirPath === "/" ? "/.relayfile.acl" : `${dirPath}/.relayfile.acl`;
13-
const url = new URL(
14-
`${normalizedBaseUrl}/v1/workspaces/${encodeURIComponent(workspace)}/fs/file`,
15-
);
16-
url.searchParams.set("path", aclPath);
11+
// Build all ACL files and write via bulk endpoint (no If-Match required)
12+
const files = Object.entries(acl).map(([dirPath, rules]) => ({
13+
path: dirPath === "/" ? "/.relayfile.acl" : `${dirPath}/.relayfile.acl`,
14+
content: JSON.stringify({ semantics: { permissions: rules } }),
15+
encoding: "utf-8" as const,
16+
}));
1717

18-
const response = await fetch(url, {
19-
method: "PUT",
20-
headers: {
21-
authorization: `Bearer ${token}`,
22-
"content-type": "application/json",
23-
},
24-
body: JSON.stringify({
25-
content: JSON.stringify({ semantics: { permissions: rules } }),
26-
encoding: "utf-8",
27-
}),
28-
});
18+
if (files.length === 0) return;
2919

30-
if (!response.ok) {
31-
const body = await response.text().catch(() => "");
32-
throw new Error(`failed to seed ACL for ${dirPath}: HTTP ${response.status} ${body}`.trim());
33-
}
20+
const url = `${normalizedBaseUrl}/v1/workspaces/${encodeURIComponent(workspace)}/fs/bulk`;
21+
const response = await fetch(url, {
22+
method: "POST",
23+
headers: {
24+
authorization: `Bearer ${token}`,
25+
"content-type": "application/json",
26+
"x-correlation-id": `seed-acl-${Date.now()}`,
27+
},
28+
body: JSON.stringify({ files }),
29+
});
30+
31+
if (!response.ok) {
32+
const body = await response.text().catch(() => "");
33+
throw new Error(`failed to seed ACLs: HTTP ${response.status} ${body}`.trim());
34+
}
35+
36+
const result = await response.json() as { errorCount: number; errors: any[] };
37+
if (result.errorCount > 0) {
38+
throw new Error(`ACL seeding had ${result.errorCount} error(s): ${JSON.stringify(result.errors)}`);
3439
}
3540
}
3641

scripts/relay/relay.sh

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -656,9 +656,7 @@ cmd_provision() {
656656
if dotfiles_exist; then
657657
local compiled_bundle summary_json ignored_count readonly_count
658658
compiled_bundle="$(build_compiled_acl_bundle "${config_json}" "${workspace}")"
659-
if [[ "${EFFECTIVE_CONFIG_PATH}" == "relay.yaml" ]]; then
660-
npx tsx "${SEED_ACL_TS}" --compiled-json "${compiled_bundle}" --base-url "${DEFAULT_RELAYFILE_URL}" --token "${admin_token}"
661-
fi
659+
npx tsx "${SEED_ACL_TS}" --compiled-json "${compiled_bundle}" --base-url "${DEFAULT_RELAYFILE_URL}" --token "${admin_token}"
662660
summary_json="$(<"${compiled_bundle}")"
663661
ignored_count="$(config_value "${summary_json}" 'data.summary.ignored')"
664662
readonly_count="$(config_value "${summary_json}" 'data.summary.readonly')"

0 commit comments

Comments
 (0)