/** * API token service. * * Single source of truth for the shared secret that non-loopback callers * must present to reach `/api/*` and the WebSocket endpoints. The token is * either pinned via `CREWLY_API_TOKEN` or generated once on first boot and * persisted (mode 0600) at `/api-token` so `crewly token` and * the running server agree on the value. * * Why this exists * --------------- * The REST API used to be completely unauthenticated while the server bound * every interface. On a production box the orchestrator PTY runs as root, so * `POST /api/terminal/:session/write` was a remote root shell. Loopback * callers (local skills via `api_call`, the local dashboard) keep working * with zero setup; everyone else needs this token. * * @module services/core/api-token.service */ /** Origin of the active token. */ export type ApiTokenSource = 'env' | 'file' | 'generated'; /** Result of resolving the token, including provenance for logging. */ export interface ResolvedApiToken { /** The raw token value. */ token: string; /** Where it came from. */ source: ApiTokenSource; /** Absolute path of the token file (only meaningful for `file`/`generated`). */ filePath: string; } /** * Absolute path of the persisted token file for the current process. * * @returns `/api-token` */ export declare function getApiTokenFilePath(): string; /** * Generate a fresh random token. * * @returns Hex-encoded random token (`TOKEN_BYTES` bytes → 64 hex chars) */ export declare function generateApiToken(): string; /** * Resolve the active API token with provenance. * * Priority: `CREWLY_API_TOKEN` env → `/api-token` → generate + * persist. The result is cached for the process lifetime; call * {@link resetApiTokenCache} in tests. * * @returns Token plus source and file path */ export declare function resolveApiToken(): ResolvedApiToken; /** What {@link mirrorEnvTokenToFile} did. */ export type EnvTokenMirrorResult = 'not-env' | 'written' | 'updated' | 'unchanged' | 'failed'; /** * Make `/api-token` hold the token the server actually uses * when that token is pinned by `CREWLY_API_TOKEN` (#1010 review). * * The CLI now presents the owner token on its local calls and reads it from * the environment or this file. A server whose token lives only in its * service environment (systemd on steamfun-ops) left the file missing, so * `crewly onboard` / `bundle` / `desktop` from an ordinary shell got 401 on * the owner-only routes. Called once at startup. * * - Token not from the environment: nothing to do (the file is the source). * - File missing: written, mode 0600. * - File holding another value: rewritten — the server only accepts the * env token, so a stale file can only produce 401s. * * @returns What was done */ export declare function mirrorEnvTokenToFile(): EnvTokenMirrorResult; /** * The token a client on this machine should present, without creating one: * `CREWLY_API_TOKEN`, else the token file, else null. Used by the CLI so a * command run before the first boot does not mint a token as a side effect. * * @returns The token, or null when none exists yet */ export declare function readExistingApiToken(): string | null; /** * Convenience accessor for the raw token. * * @returns The active API token */ export declare function getApiToken(): string; /** * Short, non-reversible identifier of a token for audit trails. * * @param token - Token to fingerprint (defaults to the active token) * @returns First `FINGERPRINT_HEX_LENGTH` hex chars of sha256(token) */ export declare function getApiTokenFingerprint(token?: string): string; /** * Constant-time comparison of a presented token against the active one. * * @param candidate - Token presented by the caller (may be missing) * @returns True when the candidate matches exactly */ export declare function verifyApiToken(candidate: string | null | undefined): boolean; /** * Drop the in-process cache so the next resolve re-reads env/file. * Intended for tests. */ export declare function resetApiTokenCache(): void; //# sourceMappingURL=api-token.service.d.ts.map