export type EnsureResult = { kind: 'cached'; token: string; } | { kind: 'no-security'; } | { kind: 'requests-disabled'; } | { kind: 'pending'; requestId: string; href: string; } | { kind: 'already-pending'; } | { kind: 'error'; message: string; }; export interface EnsureOptions { dataDir: string; signalkPort: number; /** * True when the local Signal K server is TLS-enabled. Switches the * access-request POST to `https://127.0.0.1` with verification * disabled (see `requestJson`). Defaults to false (plain HTTP) to * preserve the historical path. */ ssl?: boolean; clientId: string; description: string; permissions?: 'readonly' | 'readwrite' | 'admin'; } /** * Read a cached token from `${dataDir}/signalk-token` if present and * non-empty (after trim). Returns `undefined` otherwise. This is the * fast path — the token, once admin-approved, is stable across restarts. */ export declare function readCachedToken(dataDir: string): string | undefined; /** * Read an OPTIONAL GitHub token for the version-list endpoint's REST * calls. Unauthenticated GitHub REST is capped at 60 requests/hour per * IP; a boat sharing a WAN IP with other GitHub-polling plugins can * exhaust that, which hides the PR test images from the version dropdown. * A token raises the cap to 5000/hour and cures the exhaustion. * * The token is optional — absent, the calls stay unauthenticated (no * regression) — and is NOT a user-facing config field. Sources, first * hit wins: `GITHUB_TOKEN`, then `GH_TOKEN`, then a host-only * `${dataDir}/github-token` file (same private per-instance dir as the * Signal K token). It is read plugin-side only (Node in the SK process) * and never handed to the container, so the container-UID readability * concern that forces the SK token through an env var does not apply — a * host 0600 file is fine. A fine-grained PAT with public-repo read is * enough since the repo is public; even a zero-scope token lifts the cap. */ export declare function readGithubToken(dataDir: string): string | undefined; /** * The absolute host-side path of the cached token file under `dataDir`. * Returned regardless of whether the file currently exists — callers * who only want to mount it should pair this with `hasCachedToken`. */ export declare function tokenFilePath(dataDir: string): string; /** True iff the cached token file exists on disk. Cheaper than reading. */ export declare function hasCachedToken(dataDir: string): boolean; /** * Persist `token` to `${dataDir}/signalk-token` with mode 0600. The * single file ensures the on-disk format stays simple (no JSON parsing * to corrupt) and the mode keeps the secret readable only to the * Signal K server process owner. */ export declare function writeCachedToken(dataDir: string, token: string): void; /** Remove the cached token (e.g. after the server revoked it). No-op if absent. */ export declare function deleteCachedToken(dataDir: string): void; export interface ValidateOptions { token: string; signalkPort: number; ssl: boolean; } /** * Check whether a cached token is still accepted by the local Signal K * server. Returns: * - `valid` — the server accepted the bearer (HTTP 2xx) * - `revoked` — the server rejected it (HTTP 401/403): the admin revoked * or it expired, so the cache should be dropped and re-requested * - `unknown` — could not tell (network error, SK still starting, other * status). Treated as "keep using it" so a transient blip * doesn't throw away a good token. * * Hits `/signalk/v1/api/vessels/self` — a read every approved device token * may perform — over the same loopback transport the token flow uses. */ export declare function validateCachedToken(opts: ValidateOptions): Promise<'valid' | 'revoked' | 'unknown'>; /** * Begin a Signal K device access request and resolve when it transitions * out of PENDING. Returns one of: * - `{ kind: 'cached', token }` when the cache was hit before this call * was made (callers should usually check `readCachedToken` first; this * is a safety net). * - `{ kind: 'no-security' }` when SK has security disabled — no token * is needed; callers should connect anonymously. * - `{ kind: 'requests-disabled' }` when SK is secured but the admin has * turned off device access requests; surface this so the user can * either enable it or paste a token by hand. * - `{ kind: 'pending', requestId, href, cancel }` once SK has accepted * the request and is waiting for admin approval. The caller can: * - await `awaitApproval(href, log)` to block until decision * - call `cancel()` to stop the in-flight poller on plugin stop() * - `{ kind: 'error', message }` on any other failure path. * * Polling is server-side via `GET /signalk/v1/requests/:id`; we don't try * to subscribe to SK's notification path because the access-request flow * is intentionally HTTP-driven. */ export declare function beginTokenRequest(opts: EnsureOptions): Promise; /** * Poll `${href}` until it transitions out of PENDING, then return the * issued token. Stops if `isCancelled()` returns true between polls (so * plugin.stop() can break the loop). Returns `undefined` if the request * is denied or expires server-side. * * The href returned by SK is a relative path like * `/signalk/v1/requests/`; we resolve it against * `${scheme}://127.0.0.1:${signalkPort}` (https when `ssl`). * * `pollIntervalMs` defaults to 5s in production (an admin clicking * approve is a slow human action; we don't need to poll faster than * that). Tests override it to single-digit milliseconds. * * `ssl` is a trailing optional so the existing positional callers keep * working; defaults to plain HTTP. */ export declare function awaitApproval(href: string, signalkPort: number, isCancelled: () => boolean, log: (msg: string) => void, pollIntervalMs?: number, ssl?: boolean): Promise; //# sourceMappingURL=signalk-token.d.ts.map