fix(federation): harness round-2 — email validation + host-side URL rewrite
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

- Bug-1: replace whitespace in admin email local-part (was breaking @IsEmail)
- Bug-2: rewrite enrollment URL to use host-accessible base in seed.ts (in-cluster URL not resolvable from host)
- Bug-3: correct README Known Limitations section
- eslint.config.mjs: add tools/federation-harness/*.ts to allowDefaultProject so pre-commit hook can lint harness scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jarvis
2026-04-23 20:50:58 -05:00
parent cb118a53d9
commit 4cf9362e75
3 changed files with 32 additions and 13 deletions

View File

@@ -195,9 +195,7 @@ async function bootstrapAdmin(
// 1. Check status
const statusRes = await fetch(`${baseUrl}/api/bootstrap/status`);
if (!statusRes.ok) {
throw new Error(
`[seed] GET ${baseUrl}/api/bootstrap/status → ${statusRes.status.toString()}`,
);
throw new Error(`[seed] GET ${baseUrl}/api/bootstrap/status → ${statusRes.status.toString()}`);
}
const status = (await statusRes.json()) as { needsSetup: boolean };
@@ -214,7 +212,7 @@ async function bootstrapAdmin(
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: `Harness Admin (${label})`,
email: `harness-admin-${label.toLowerCase()}@example.invalid`,
email: `harness-admin-${label.toLowerCase().replace(/\s+/g, '-')}@example.invalid`,
password,
}),
});
@@ -344,9 +342,18 @@ async function enrollGrant(opts: {
});
console.log(`[seed] Created peer on A: ${peerA.peerId}`);
// 5. Redeem token at Server B's enrollment endpoint with A's CSR
// 5. Redeem token at Server B's enrollment endpoint with A's CSR.
// The enrollment endpoint is not admin-guarded — the one-time token IS the credential.
const redeemUrl = tokenResult.enrollmentUrl;
//
// The enrollmentUrl returned by the gateway is built using BETTER_AUTH_URL which
// resolves to the in-cluster Docker hostname (gateway-b:3000). That URL is only
// reachable from other containers, not from the host machine running this script.
// We rewrite the host portion to use the host-accessible serverBUrl so the
// seed script can reach the endpoint from the host.
const parsedEnrollment = new URL(tokenResult.enrollmentUrl);
const tokenSegment = parsedEnrollment.pathname.split('/').pop()!;
const redeemUrl = `${serverBUrl}/api/federation/enrollment/${tokenSegment}`;
console.log(`[seed] Rewritten redeem URL (host-accessible): ${redeemUrl}`);
const redeemRes = await fetch(redeemUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },