feat(gateway): add node capability negotiation foundation

This commit is contained in:
William Valentin
2026-02-16 12:14:25 -08:00
parent de0c1f41b3
commit d9f7807ab2
17 changed files with 675 additions and 7 deletions
+40
View File
@@ -13,6 +13,14 @@ export interface AuthResult {
error?: string;
}
export interface NodeAuthScopeConfig {
enabled: boolean;
method: string;
nodeRole?: string;
allowedRoles?: string[];
roleScopes?: Record<string, string[]>;
}
/**
* Authenticates a WebSocket upgrade request or HTTP request.
*
@@ -69,6 +77,38 @@ export function authenticateRequest(req: IncomingMessage, config: AuthConfig): A
return { authenticated: true, identity: 'anonymous' };
}
export function authorizeNodeMethod(config: NodeAuthScopeConfig): AuthResult {
if (!config.method.startsWith('node.')) {
return { authenticated: true };
}
if (!config.enabled) {
return { authenticated: false, error: 'Node RPC is disabled' };
}
if (config.method === 'node.register') {
return { authenticated: true };
}
if (!config.nodeRole) {
return { authenticated: false, error: 'Node not registered for this connection' };
}
const allowedRoles = config.allowedRoles ?? [];
if (allowedRoles.length > 0 && !allowedRoles.includes(config.nodeRole)) {
return { authenticated: false, error: `Node role '${config.nodeRole}' is not allowed` };
}
const defaultScopes = ['node.capabilities.get'];
const roleScopes = config.roleScopes ?? {};
const permitted = roleScopes[config.nodeRole] ?? defaultScopes;
if (!permitted.includes(config.method)) {
return { authenticated: false, error: `Method '${config.method}' is not permitted for node role '${config.nodeRole}'` };
}
return { authenticated: true };
}
function extractQueryToken(req: IncomingMessage): string | undefined {
try {
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);