/** * Machine registration with the Aexol backend. * * Contract (Batch 1 backend): * POST /api/machines/register * Headers: Authorization: Bearer * Body: { name?: string, hostname: string, version: string, pid: number } * 200: { machineId: string, jwt: string, name: string, ownerId?: string } * * Auth priority: * - If `userJwt` is set → used as Bearer token (machine gets ownerId) * - Else → `apiKey` (team API key, machine gets no owner) * * Why this lives in its own module: * - It's the ONLY consumer of the team API key / user JWT for registration. * Once registration succeeds, the auth token is dropped on the floor — * only the `machineJwt` is persisted (in `machine.json`) and threaded * into `RelayClient`. Keeps the blast radius of an in-memory leak minimal. * - It owns JWT expiry decoding so `serve.ts` doesn't have to know about * JWT internals; a single boundary for "is the saved record still good". * * Failure mode: registration is fail-fast (no retry). The user is sitting * at a CLI prompt; if their team key is wrong or the backend is down, the * right answer is a clear error message, not a hang. WS reconnect (which * IS retried) is the long-running path; that's `RelayClient`'s job. */ import { type MachineRecord } from "./machine-store.js"; export interface RegistrationDeps { /** Backend base URL, e.g. "https://api.aexol.ai". No trailing slash. */ backendUrl: string; /** Team API key — used when `userJwt` is not set. */ apiKey: string; /** Optional user JWT from OAuth login. When set, used instead of `apiKey` for registration. */ userJwt?: string; /** Display name override (from `--machine-name`). Falls back to hostname. */ machineNameOverride?: string; /** CLI version string for the registration payload. */ version: string; /** Process id (informational; backend uses for last-seen audit). */ pid?: number; /** * Skew tolerance: treat the JWT as expired if it expires in less than this * many seconds. Default 60s — well below any realistic JWT lifetime, but * large enough that we don't connect with a token that dies mid-handshake. */ expirySkewSec?: number; /** Override fetch (tests). */ fetchImpl?: typeof fetch; /** * Force a fresh registration even when the cached `machine.json` JWT has not * expired. Used after the relay reports an auth-failed event or after * cloud-agent discovery returns an auth-rejection — both indicate the cached * JWT is rejected by the backend (e.g. secret rotation) even though its * `exp` claim is still in the future. Without this, `ensureMachineRegistered` * would silently reuse the stale token forever and never recover. */ forceRefresh?: boolean; } export interface RegisterResponse { /** True when the existing on-disk record was reused (no network call). */ reused: boolean; /** * True when the cache was skipped because the caller forced a refresh * (e.g. after the relay reported an auth-failed event, or because * cloud-agent discovery reported the cached JWT was rejected). Always * implies `reused === false`. */ forced?: boolean; record: MachineRecord; } /** * Decode the `exp` claim of a JWT without verifying the signature. * We're not using this for AuthN — only for "should we re-register * proactively". The backend is the source of truth on validity; if our * cheap pre-check is wrong, the WS handshake will reject and we'll fall * through to a full re-register on the next `spectral serve`. * * Returns `null` if the token isn't a parseable JWT or has no `exp`. */ export declare function decodeJwtExp(jwt: string): number | null; /** * Decode the `sub` (subject) claim from a JWT payload without signature * verification. Used to compare the currently logged-in user identity against * the `ownerId` stored in `machine.json` so we can detect account switches * and force re-registration (which triggers the backend ownership transfer). */ export declare function decodeJwtSub(jwt: string): string | null; /** True when the JWT is past expiry (with skew). Treats undecodable as expired. */ export declare function isJwtExpired(jwt: string, skewSec?: number): boolean; /** * Ensure a machine is registered. Returns the record either from the cached * `machine.json` (when the JWT is still fresh) or from a freshly issued one. * * Re-registration triggers: * - No `machine.json` on disk (first run). * - The cached JWT is expired or near-expired. * * Why we don't re-register on `--machine-name` change: the CLI flag controls * the *next* registration's display name, but switching it shouldn't burn a * fresh `machineId` on every restart. If users want a clean break they can * delete `machine.json`. */ export declare function ensureMachineRegistered(deps: RegistrationDeps): Promise; //# sourceMappingURL=registration.d.ts.map