/** * Where an installation's credential lives, and why it is not in the project. * * `.crossline/` would be the obvious place and is the wrong one. It ignores * itself, but a founder has already committed it once in this project's * history — that is a recorded defect, not a hypothetical — and the cost of * that mistake is different for a report than for a credential. A report in a * public repository is embarrassing; a token in a public repository is the * incident this product exists to prevent. * * So the credential goes in the user's own configuration directory, outside * any repository, mode 0600. Nothing a `git add .` can reach. * * In production there is no home directory worth writing to and no interactive * enrollment: the platform holds the token as an environment variable, which is * how Vercel, Fly and Railway all expect a secret to arrive. That is why the * environment is read first and wins. */ /** The variable a deployment sets. Checked before any file. */ export declare const TOKEN_ENV = "CROSSLINE_TOKEN"; export interface Credential { token: string; /** Opaque id for this installation, for the service's own logs. */ installationId: string; /** Which service issued it, so a staging token cannot be sent to production. */ service: string; enrolledAt: string; } export type CredentialSource = { kind: "environment"; variable: string; } | { kind: "file"; path: string; }; export interface ResolvedCredential { token: string; source: CredentialSource; /** One line naming where it came from. Never contains the token. */ provenance: string; } /** `~/.config/crossline/credentials.json`, honouring XDG when it is set. */ export declare function credentialPath(env?: NodeJS.ProcessEnv): string; /** * The token for this installation, or null when it has never been enrolled. * * Deliberately does not throw on an unreadable or malformed file. A missing * credential is an ordinary state — every un-enrolled installation is in it — * and the caller has a better message for it than a parse error. */ export declare function readCredential(env?: NodeJS.ProcessEnv): ResolvedCredential | null; /** * Write the credential, readable by nobody else. * * The mode is set on the file itself rather than left to the umask, and it is * set again on an existing file, because a token written once under a loose * umask stays loose forever otherwise. */ export declare function writeCredential(credential: Credential, env?: NodeJS.ProcessEnv): string; /** True when the stored credential is readable only by its owner. */ export declare function isPrivate(path: string): boolean; /** * An enrollment that has begun and is waiting on a human. * * It has to survive between two tool calls, because approval takes as long as * it takes and a tool call that blocks for fifteen minutes is not something an * agent can drive. The device code is the secret half of the exchange, so this * lives beside the credential — same private directory, same 0600 — rather * than being handed back to the caller to pass in again. An agent that has to * carry the device code between calls is an agent with the secret in its * context, which is the thing the whole flow exists to avoid. */ export interface PendingEnrollment { deviceCode: string; userCode: string; verificationUri: string; verificationUriComplete?: string; interval: number; /** Absolute, so a resumed poll knows whether it is already too late. */ expiresAt: string; service: string; } export declare function writePending(pending: PendingEnrollment, env?: NodeJS.ProcessEnv): string; export declare function readPending(env?: NodeJS.ProcessEnv): PendingEnrollment | null; /** Remove it, whether it was approved, refused or abandoned. */ export declare function clearPending(env?: NodeJS.ProcessEnv): void;