fix(agent): always spawn agent with pre-auth key and auto-approve

- Generate a fresh pre-auth key for every agent startup
- Preserve existing tailscale state to avoid creating a new host
- Auto-approve pending auth requests via /api/v1/auth/approve
- Show approval link in settings UI as fallback

Closes HP-558
This commit is contained in:
Aarnav Tale
2026-07-13 10:58:35 -04:00
parent 3d9b9b0f7e
commit b3c0c1c691
8 changed files with 155 additions and 18 deletions
+3
View File
@@ -22,6 +22,7 @@ import log from "~/utils/log";
import { type Capabilities, capabilitiesFor } from "./capabilities";
import { isDataWithApiError } from "./error-client";
import { type ApiKeyApi, makeApiKeyApi } from "./resources/api-keys";
import { type AuthApi, makeAuthApi } from "./resources/auth";
import { makeNodeApi, type NodeApi } from "./resources/nodes";
import { makePolicyApi, type PolicyApi } from "./resources/policy";
import { makePreAuthKeyApi, type PreAuthKeyApi } from "./resources/pre-auth-keys";
@@ -48,6 +49,7 @@ export interface HeadscaleClient {
policy: PolicyApi;
preAuthKeys: PreAuthKeyApi;
apiKeys: ApiKeyApi;
auth: AuthApi;
}
export interface CreateHeadscaleOptions {
@@ -149,6 +151,7 @@ export async function createHeadscale(opts: CreateHeadscaleOptions): Promise<Hea
policy: makePolicyApi(transport, capabilities, apiKey),
preAuthKeys: makePreAuthKeyApi(transport, capabilities, apiKey),
apiKeys: makeApiKeyApi(transport, capabilities, apiKey),
auth: makeAuthApi(transport, capabilities, apiKey),
};
},
async dispose() {
@@ -0,0 +1,27 @@
import type { Capabilities } from "../capabilities";
import type { Transport } from "../transport";
export interface AuthApi {
/**
* Approve a pending Headscale authentication request.
* Used by the Headplane agent to auto-approve its own registration.
*/
approve(authId: string): Promise<void>;
}
export function makeAuthApi(
transport: Transport,
_capabilities: Capabilities,
apiKey: string,
): AuthApi {
return {
approve: async (authId) => {
await transport.request({
method: "POST",
path: "v1/auth/approve",
apiKey,
body: { authId },
});
},
};
}