/**
 * Request guards for agent-app routes: session auth (302 redirect for pages,
 * JSON 401 for APIs), admin allowlisting (404 — the route stays invisible to
 * non-admins), and the billable-balance gate (402 with a stable code).
 * Session resolution is a seam; thrown Responses follow the router convention
 * of surfacing a thrown Response as the route result.
 */
/** Define options for configuring authentication guard behavior and session retrieval */
interface AuthGuardOptions<Session> {
    /** e.g. a better-auth `auth.api.getSession` wrapped by the app. */
    getSession(request: Request): Promise<Session | null | undefined>;
    /** Default '/login'. */
    loginPath?: string;
}
/** Resolve user authentication and session requirements for page and API requests */
interface AuthGuard<Session> {
    /** Page guard — throws a 302 redirect Response to `loginPath`. */
    requireUser(request: Request): Promise<Session>;
    /** API guard — throws JSON 401 `{ error: 'Unauthorized', code: 'auth.unauthenticated' }`. */
    requireApiUser(request: Request): Promise<Session>;
    /** `apiResponse` selects the 401 JSON path over the redirect. */
    requireSession(request: Request, opts?: {
        apiResponse?: boolean;
    }): Promise<Session>;
    getOptionalSession(request: Request): Promise<Session | null>;
}
/** Create an authentication guard that enforces session presence and handles unauthorized access responses */
declare function createAuthGuard<Session>(opts: AuthGuardOptions<Session>): AuthGuard<Session>;
/** Resolve a value or an HTTP response indicating failure in a guarded operation */
type GuardResolution<T> = {
    ok: true;
    value: T;
} | {
    ok: false;
    response: Response;
};
/**
 * Adapt a guard that THROWS a Response (the quartet above — the router
 * convention) to the `{ok: true, value} | {ok: false, response}` resolution
 * shape the route factories take (`/chat-routes` `authorize`,
 * `/interactions` `resolveConnection`, `/chat-routes` upload `authorize`).
 * Every product wrote this try/catch by hand; it lives here once.
 */
declare function guardResolution<T>(run: () => Promise<T>): Promise<GuardResolution<T>>;
/** Comma/whitespace separated → trimmed, lowercased, empties dropped. */
declare function parseAdminEmails(raw: string | null | undefined): string[];
/** Define options to resolve user session and control access based on allowed admin emails */
interface AdminGuardOptions<Session> {
    requireUser(request: Request): Promise<Session>;
    emailOf(session: Session): string | null | undefined;
    /** Resolved per request; an EMPTY allowlist refuses everyone. */
    allowedEmails(): string[];
}
/** Non-admins (and empty allowlists) get 404, keeping the route invisible —
 *  better than a "forbidden" footprint that advertises its existence. */
declare function createAdminGuard<Session>(opts: AdminGuardOptions<Session>): (request: Request) => Promise<Session>;
/** Describe the billable balance state including overage permission and remaining USD balance */
interface BillableBalanceState {
    overageAllowed: boolean;
    remainingBalanceUsd: number;
}
/** Define options to assert and customize billable balance enforcement behavior */
interface AssertBillableBalanceOptions {
    env?: Record<string, string | undefined>;
    /** App-specific enforcement override flag (e.g. 'GTM_BILLING_ENFORCEMENT'),
     *  fed to `isTangleBillingEnforcementDisabled`. */
    enforcementEnvVar?: string;
    /** Default 'Add balance or upgrade your plan to invoke this agent.'. */
    errorMessage?: string;
    /** Merged into the 402 JSON body (e.g. `{ organizationId }`). */
    errorBody?: Record<string, unknown>;
}
/**
 * Gate a billable turn: passes when enforcement is disabled (dev default),
 * the tier allows overage, or remaining balance is positive. Otherwise throws
 * a 402 Response with the stable `billing.balance_required` code so clients
 * can route to the billing screen.
 */
declare function assertBillableBalance(state: BillableBalanceState, opts?: AssertBillableBalanceOptions): void;

/**
 * Cross-site Tangle SSO for agent apps: signed-state CSRF cookies plus the
 * full start/callback orchestration against the platform's /cross-site
 * bridge. The platform wire client and account persistence are structural
 * seams (`TangleSsoAuthClient` / `TangleSsoAccountStore`), so this module
 * never imports agent-runtime, an auth framework, or a database driver.
 * WebCrypto only — runs in workerd without node compatibility flags.
 */
/** Define configuration options for managing SSO state including secret, lifetime, and clock injection */
interface SsoStateConfig {
    /** HMAC-SHA256 secret (e.g. the app's auth secret). */
    secret: string;
    /** State lifetime in ms. Default 600 000. */
    ttlMs?: number;
    /** Injectable clock (ms since epoch). Default Date.now. */
    now?: () => number;
}
/** Mint a `<randomHex32>.<timestamp36>.<hmacHex>` state value. The timestamp
 *  is inside the signed payload, so expiry survives cookie-attribute tampering. */
declare function createSignedSsoState(config: SsoStateConfig): Promise<string>;
/** Verify the MAC (constant-time) and the signed TTL. */
declare function verifySignedSsoState(state: string, config: SsoStateConfig): Promise<boolean>;
/** Describe the result of exchanging SSO credentials including API key, user info, and optional plan details */
interface TangleSsoExchangeResult {
    apiKey: string;
    user: {
        id: string;
        email: string;
        name?: string | null;
    };
    plan?: {
        tier: string;
    } | null;
}
/** Structural mirror of the platform auth wire client — any object with these
 *  two methods satisfies it without this module importing the concrete class. */
interface TangleSsoAuthClient {
    authorizeUrl(options: {
        state: string;
        redirectUri?: string;
    }): string;
    exchange(code: string): Promise<TangleSsoExchangeResult>;
}
/** Thrown by `upsertUserByEmail` when the app-local user row cannot be
 *  created; the callback handler maps it to `?error=tangle_user_create_failed`.
 *  Any other store error propagates. */
declare class TangleSsoUserCreateError extends Error {
    constructor(message?: string);
}
/**
 * Account persistence seam. Covers both storage styles in use: link-table
 * apps (a per-user platform-link row) and session-column apps (the key on the
 * session row) — `saveTangleLink` receives both `userId` and `sessionToken`,
 * and each app persists with the key it needs. `createSession` runs first so
 * the token is always available to `saveTangleLink`.
 */
interface TangleSsoAccountStore {
    /** Find-or-create the app-local user. `tangleUserId` is the platform's
     *  stable user id — match on it first when the app stores it (emails are
     *  mutable on the platform; the id is not), falling back to email for
     *  first-time logins. */
    upsertUserByEmail(input: {
        email: string;
        name: string | null;
        tangleUserId: string;
    }): Promise<{
        userId: string;
    }>;
    /** Create an app session row; returns the session-cookie token value. */
    createSession(input: {
        userId: string;
        expiresAt: Date;
        ipAddress: string | null;
        userAgent: string | null;
    }): Promise<{
        token: string;
    }>;
    /** Persist the platform link (API key + platform identity). */
    saveTangleLink(input: {
        userId: string;
        sessionToken: string;
        tangleUserId: string;
        email: string;
        name: string | null;
        apiKey: string;
        planTier: string | null;
    }): Promise<void>;
}
/** Successful-login context handed to the `setSessionCookie` seam. */
interface TangleSsoSessionCookieArgs {
    /** Session token returned by `store.createSession`. */
    token: string;
    /** Session expiry (now + `sessionTtlSeconds`). */
    expiresAt: Date;
    /** Mirrors `sessionTtlSeconds` after defaulting. */
    ttlSeconds: number;
    /** Mirrors `TangleSsoHandlerOptions.secureCookies`. */
    secure: boolean;
}
/**
 * Sign a session token to better-call's signed-cookie contract — the value
 * better-auth's `getSignedCookie` verifies: `<token>.<signature>` where the
 * signature is the raw HMAC-SHA256 of the token under `secret`, encoded as
 * STANDARD base64 WITH padding (32 bytes → 44 chars ending `=`; better-call
 * rejects any other length or suffix, so url-safe/unpadded variants read back
 * as a null session). The joined value is percent-encoded once at cookie
 * serialization, matching better-call's `serializeSignedCookie` byte-exactly.
 */
declare function signSessionCookieValue(token: string, secret: string): Promise<string>;
/** Structural slice of a `betterAuth()` instance — only what cookie minting
 *  reads. No better-auth import: the signing contract is implemented by
 *  `signSessionCookieValue`, byte-compatible with better-auth's own
 *  `makeSignature`. */
interface BetterAuthSessionCookieSource {
    $context: PromiseLike<{
        secret: string;
        authCookies: {
            sessionToken: {
                /** Final cookie name — better-auth decides the `__Secure-` prefix
                 *  (and any `advanced.cookiePrefix`) once at `betterAuth()` init. */
                name: string;
                attributes: {
                    secure?: boolean;
                    sameSite?: string;
                    path?: string;
                    httpOnly?: boolean;
                    domain?: string;
                };
            };
        };
    }>;
}
/** Define options to customize warning behavior for shadowed cookie names in authentication sessions */
interface BetterAuthSessionCookieMinterOptions {
    /** Receives the shadowed-cookie-name warning (see below). Default
     *  console.warn. */
    warn?: (message: string) => void;
}
/**
 * Canonical `setSessionCookie` wiring for better-auth apps: mint the session
 * Set-Cookie exactly as better-auth's own login flows do — name + attributes
 * from `auth.$context.authCookies.sessionToken` (better-auth stays
 * authoritative over prefix/name/attributes) and the value signed to
 * better-call's `getSignedCookie` contract. A raw unprefixed
 * `better-auth.session_token` left by an earlier login is explicitly expired
 * so it cannot shadow the real cookie.
 *
 * Warns when the app's session cookie still has better-auth's DEFAULT name:
 * the Tangle platform (id.tangle.tools) sets a `Domain=.tangle.tools` cookie
 * under that exact name, and equal-path cookies are sent oldest-first — the
 * platform's cookie is always older (the user signs in there before the app's
 * callback runs), so the app reads the platform's token, fails its own
 * signature check, and every fresh login lands logged-out. Per-app
 * `advanced.cookiePrefix` is the fix.
 *
 * Throws on a domain-scoped session cookie for the same reason: a
 * `Domain=`-wide session cookie is exactly the shadowing footgun.
 */
declare function createBetterAuthSessionCookieMinter(auth: BetterAuthSessionCookieSource, options?: BetterAuthSessionCookieMinterOptions): (args: TangleSsoSessionCookieArgs) => Promise<string[]>;
/** Define configuration options for handling Tangle SSO authentication and session management */
interface TangleSsoHandlerOptions {
    auth: TangleSsoAuthClient;
    store: TangleSsoAccountStore;
    /** HMAC secret for the state cookie. */
    stateSecret: string;
    /** Absolute callback URL registered with the platform. */
    callbackUrl: string;
    stateCookieName: string;
    /** Default 'better-auth.session_token'. Ignored when `setSessionCookie` is
     *  provided. The default path prepends `__Secure-` iff `secureCookies`. */
    sessionCookieName?: string;
    /** Mint the host auth framework's own session cookie(s); return complete
     *  Set-Cookie header values (the handler appends them verbatim and sets no
     *  session cookie itself). Supply this when the framework should stay
     *  authoritative over name/prefix/signing/attributes — e.g. better-auth:
     *  `auth.$context.authCookies.sessionToken` + `makeSignature`. */
    setSessionCookie?: (args: TangleSsoSessionCookieArgs) => readonly string[] | Promise<readonly string[]>;
    /** HMAC-SHA256 secret the host auth framework verifies session cookies with
     *  (better-auth: its `secret`). Required when `setSessionCookie` is absent —
     *  the default cookie is minted to better-call's signed contract via
     *  `signSessionCookieValue`; an unsigned or mis-signed value reads back as a
     *  null session, so there is deliberately no fallback to `stateSecret`
     *  (which is not guaranteed to be the auth secret). */
    sessionCookieSecret?: string;
    /** Adds `Secure` to every cookie this module sets, and (default session
     *  cookie only) the `__Secure-` name prefix. Must match the auth
     *  framework's own secure-cookie decision (better-auth: https `baseURL` /
     *  `advanced.useSecureCookies`), or it will look up a different cookie name
     *  than the one set here. */
    secureCookies: boolean;
    /** Default 604 800 (7 days). */
    sessionTtlSeconds?: number;
    /** Default 600. Applies to both the cookie Max-Age and the signed TTL. */
    stateTtlSeconds?: number;
    /** Default '/app'. */
    defaultRedirectPath?: string;
    /** Default '/login'. */
    loginPath?: string;
    /** Failure log hook (e.g. console.error). Default no-op. */
    log?: (message: string, error?: unknown) => void;
    now?: () => number;
}
/** Define handlers for SSO start and callback routes managing authentication flow and session cookies */
interface TangleSsoHandlers {
    /** GET start route: mint + sign state, set the state cookie, 302 to the
     *  platform authorize URL. `?redirect=` carries the post-login path. */
    start(request: Request): Promise<Response>;
    /** GET callback route: verify state, exchange the code, upsert the user,
     *  create the session, save the platform link, set the session cookie
     *  (via the `setSessionCookie` seam, else signed to better-call's contract
     *  with `sessionCookieSecret`), 302 to the saved redirect. Every failure
     *  302s to `loginPath?error=…` with the state cookie cleared. */
    callback(request: Request): Promise<Response>;
}
/** Create Tangle SSO handlers to manage authentication state, callbacks, and session cookies */
declare function createTangleSsoHandlers(opts: TangleSsoHandlerOptions): TangleSsoHandlers;

export { type AuthGuard as A, type BetterAuthSessionCookieMinterOptions as B, type GuardResolution as G, type SsoStateConfig as S, type TangleSsoHandlers as T, type TangleSsoAuthClient as a, type TangleSsoAccountStore as b, type AdminGuardOptions as c, type AssertBillableBalanceOptions as d, type AuthGuardOptions as e, type BetterAuthSessionCookieSource as f, type BillableBalanceState as g, type TangleSsoExchangeResult as h, type TangleSsoHandlerOptions as i, type TangleSsoSessionCookieArgs as j, TangleSsoUserCreateError as k, assertBillableBalance as l, createAdminGuard as m, createAuthGuard as n, createBetterAuthSessionCookieMinter as o, createSignedSsoState as p, createTangleSsoHandlers as q, guardResolution as r, parseAdminEmails as s, signSessionCookieValue as t, verifySignedSsoState as v };
