feat(client): add keypair generation

This commit is contained in:
nav
2026-04-08 21:34:38 +00:00
parent fb39a17dbb
commit fc226b1f18
3 changed files with 155 additions and 4 deletions

View File

@@ -9,11 +9,12 @@ import {
} from "../../../Yonexus.Protocol/src/index.js";
import type { YonexusClientConfig } from "./config.js";
import {
createInitialClientState,
ensureClientKeyPair,
hasClientKeyPair,
hasClientSecret,
type YonexusClientState,
type YonexusClientStateStore
createInitialClientState,
YonexusClientState,
YonexusClientStateStore
} from "./state.js";
import type { ClientConnectionState, ClientTransport } from "./transport.js";
@@ -66,7 +67,12 @@ export class YonexusClientRuntime {
}
this.phase = "starting";
this.clientState = await this.options.stateStore.load(this.options.config.identifier);
// Load existing state and ensure key pair exists
let state = await this.options.stateStore.load(this.options.config.identifier);
const keyResult = await ensureClientKeyPair(state, this.options.stateStore);
this.clientState = keyResult.state;
await this.options.transport.connect();
}

View File

@@ -1,5 +1,6 @@
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
import { generateKeyPair, type KeyPair } from "../crypto/keypair.js";
export const CLIENT_STATE_VERSION = 1;
@@ -132,6 +133,31 @@ export function hasClientKeyPair(state: YonexusClientState): boolean {
);
}
/**
* Ensure the client state has a valid key pair.
* If no key pair exists, generates a new Ed25519 key pair.
* Returns the (possibly updated) state and whether a new key was generated.
*/
export async function ensureClientKeyPair(
state: YonexusClientState,
stateStore: YonexusClientStateStore
): Promise<{ state: YonexusClientState; generated: boolean }> {
if (hasClientKeyPair(state)) {
return { state, generated: false };
}
const keyPair = await generateKeyPair();
const updatedState: YonexusClientState = {
...state,
privateKey: keyPair.privateKey,
publicKey: keyPair.publicKey,
updatedAt: Math.floor(Date.now() / 1000)
};
await stateStore.save(updatedState);
return { state: updatedState, generated: true };
}
function assertClientStateShape(
value: unknown,
filePath: string