-
Notifications
You must be signed in to change notification settings - Fork 7
feat(kiloclaw): forward proxy token and enable enforcement default #599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,11 @@ | ||
| import type { Context, Hono } from 'hono'; | ||
| import type { Supervisor } from '../supervisor'; | ||
|
|
||
| export function registerHealthRoute(app: Hono, supervisor: Supervisor): void { | ||
| const handler = (c: Context) => { | ||
| const stats = supervisor.getStats(); | ||
| const ready = stats.state === 'running'; | ||
| return c.json( | ||
| { | ||
| status: ready ? 'ok' : 'starting', | ||
| gateway: stats.state, | ||
| uptime: stats.uptime, | ||
| restarts: stats.restarts, | ||
| }, | ||
| ready ? 200 : 503 | ||
| ); | ||
| }; | ||
| export function registerHealthRoute(app: Hono, _supervisor: Supervisor): void { | ||
| const handler = (c: Context) => c.json({ status: 'ok' }); | ||
|
|
||
| // Public Fly health probe endpoint. Keep response intentionally minimal. | ||
| app.get('/_kilo/health', handler); | ||
| // Compatibility alias for machines still configured with legacy health path. | ||
| // Compatibility alias to match the same minimal, public health response. | ||
| app.get('/health', handler); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import { describe, expect, it } from 'vitest'; | ||
| import { deriveGatewayToken } from '../auth/gateway-token'; | ||
| import { buildForwardHeaders } from './proxy-headers'; | ||
|
|
||
| describe('buildForwardHeaders', () => { | ||
| it('adds routing and proxy token headers', async () => { | ||
| const headers = new Headers({ | ||
| host: 'example.com', | ||
| 'x-request-id': 'req-123', | ||
| }); | ||
|
|
||
| const result = await buildForwardHeaders({ | ||
| requestHeaders: headers, | ||
| machineId: 'machine-123', | ||
| sandboxId: 'sandbox-abc', | ||
| gatewayTokenSecret: 'secret-123', | ||
| }); | ||
|
|
||
| const expectedToken = await deriveGatewayToken('sandbox-abc', 'secret-123'); | ||
|
|
||
| expect(result.get('fly-force-instance-id')).toBe('machine-123'); | ||
| expect(result.get('x-kiloclaw-proxy-token')).toBe(expectedToken); | ||
| expect(result.get('host')).toBeNull(); | ||
| expect(result.get('x-request-id')).toBe('req-123'); | ||
| }); | ||
|
|
||
| it('overwrites inbound proxy token header', async () => { | ||
| const headers = new Headers({ | ||
| 'x-kiloclaw-proxy-token': 'old-token', | ||
| }); | ||
|
|
||
| const result = await buildForwardHeaders({ | ||
| requestHeaders: headers, | ||
| machineId: 'machine-123', | ||
| sandboxId: 'sandbox-abc', | ||
| gatewayTokenSecret: 'secret-123', | ||
| }); | ||
|
|
||
| const expectedToken = await deriveGatewayToken('sandbox-abc', 'secret-123'); | ||
| expect(result.get('x-kiloclaw-proxy-token')).toBe(expectedToken); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { deriveGatewayToken } from '../auth/gateway-token'; | ||
|
|
||
| export async function buildForwardHeaders(params: { | ||
| requestHeaders: Headers; | ||
| machineId: string; | ||
| sandboxId: string; | ||
| gatewayTokenSecret: string; | ||
| }): Promise<Headers> { | ||
| const { requestHeaders, machineId, sandboxId, gatewayTokenSecret } = params; | ||
| const forwardHeaders = new Headers(requestHeaders); | ||
|
|
||
| const gatewayToken = await deriveGatewayToken(sandboxId, gatewayTokenSecret); | ||
| forwardHeaders.set('x-kiloclaw-proxy-token', gatewayToken); | ||
| forwardHeaders.set('fly-force-instance-id', machineId); | ||
| forwardHeaders.delete('host'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Incomplete hop-by-hop header stripping when forwarding request headers Only
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is kinda true kinda not, for WS Connection/Upgrade are part of the handshake which is why haven't done strict hop-by-hop stripping. |
||
|
|
||
| return forwardHeaders; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WARNING: Health endpoint now always reports healthy, masking startup/crash states
This route used to return
503when the gateway was not running. Returning200/{ status: 'ok' }unconditionally can make readiness checks and external monitors treat unhealthy instances as healthy, which risks routing traffic to instances that are still starting or have crashed.