/**
 * Capability-scoped sub-token — HMAC-attenuated handoff (T6f).
 *
 * The remote-control server starts with a single parent token (the one
 * displayed in the QR / printed by `preste --remote`). For collaborator
 * handoff (`preste remote share --scope read-only --ttl 1h`), the parent
 * mints a sub-token carrying a scope and an expiry, signed with HMAC-SHA256
 * against the parent token.
 *
 * The server verifies in O(1): recompute HMAC, constant-time compare. No
 * key registry, no per-session state, no WASM. The sub-token is a JWT-like
 * `<payload>.<signature>` (base64url) but with a compact 1-version payload
 * and `parent` as the HMAC key (no key id, single-tenant).
 *
 * Scope hierarchy (lowest privilege first):
 *
 *   read-only      observe : /remote/health, /remote/state, /remote/stream
 *   approve-only   read-only + HITL : /remote/hitl/*, /remote/veto/*
 *   full           everything : adds /remote/inject (mid-run steering)
 *
 * A `full` sub-token still expires (TTL-bounded delegation). The parent
 * token itself never expires — its lifetime is the running session.
 *
 * Design choices :
 *   - HMAC-SHA256, not Biscuit. Biscuit is in the workspace (bastion-api uses
 *     `@biscuit-auth/biscuit-wasm` 0.6.0) but pulls in a WASM blob and a
 *     datalog policy language. T6f MVP doesn't need further attenuation by
 *     the delegatee — the founder hands ONE bounded token. If chained
 *     delegation arrives later, that's when Biscuit earns its weight.
 *   - base64url, not raw hex : URL-safe for QR / share UIs.
 *   - `jti` is a random 6-byte id : not used by the server (no revocation
 *     list yet), but unique per mint for log correlation.
 *
 * @public @since 2.18.0 — preste remote-control T6f
 */
import { type EcP256Jwk } from "./dpop.js";
/** Capability scopes, lowest-privilege first. */
export type SubTokenScope = "read-only" | "approve-only" | "full";
/** True iff `granted` covers `required`. */
export declare function scopeCovers(granted: SubTokenScope, required: SubTokenScope): boolean;
/** Sub-token payload shape. v1 only. */
export interface SubTokenClaims {
    /** Format version. Always 1 in v1. */
    v: 1;
    /** Capability scope granted by this token. */
    scope: SubTokenScope;
    /** Unix epoch seconds — the token MUST be rejected at or after this time. */
    exp: number;
    /** Random per-mint id (12 hex chars). For log correlation, not revocation. */
    jti: string;
    /**
     * Optional confirmation claim (RFC 7800 §3.1 + RFC 9449 §6.1). When
     * present, the bearer alone is not enough — every request must also
     * carry a DPoP proof JWT signed by the key whose JWK thumbprint is
     * `cnf.jkt`. See `remote/dpop.ts` for the validator. (P1b)
     */
    cnf?: {
        jkt: string;
    };
}
/** Options for `mintSubToken`. */
export interface MintSubTokenOptions {
    /** The parent session token (typically the one shown in the QR). */
    parentToken: string;
    /** Scope to grant. */
    scope: SubTokenScope;
    /** Time-to-live in seconds. Caller computes from a duration string upstream. */
    ttlSec: number;
    /** Optional clock injection (defaults to `Date.now()`). */
    now?: () => number;
    /**
     * Optional device-binding via RFC 7800 confirmation claim. When set,
     * the resulting sub-token is sender-constrained : the holder must
     * present a DPoP proof JWT (signed by the matching private key) on
     * every request. See `remote/dpop.ts`. (P1b)
     */
    cnf?: {
        jkt: string;
    };
}
/**
 * Mint a sub-token attenuated from `parentToken`.
 *
 * The returned string is `<payloadB64>.<sigB64>`, safe to embed in URLs / QR
 * codes. Anyone holding the parent token can verify it; nobody else can.
 */
export declare function mintSubToken(opts: MintSubTokenOptions): string;
/** Outcome of `verifySubToken`. */
export interface VerifySubTokenResult {
    valid: boolean;
    /** Present iff `valid === true`. */
    scope?: SubTokenScope;
    /** Present iff `valid === false`. Brief reason for logs / metrics. */
    reason?: string;
    /** Present iff `valid === true`. Useful for log correlation. */
    jti?: string;
    /**
     * Present iff `valid === true` AND the token carries a `cnf` claim
     * (P1b device-binding). When set, the caller MUST also validate a
     * DPoP proof JWT against `cnf.jkt` via `validateDpopProof` before
     * granting the scope. (Bearer-only acceptance of a device-bound
     * sub-token would defeat the binding.)
     */
    cnf?: {
        jkt: string;
    };
}
/**
 * Verify a sub-token against `parentToken`. Returns scope on success, or
 * a typed reason on failure. NEVER throws on malformed input — the caller
 * decides whether to log, deny, or surface to the user.
 */
export declare function verifySubToken(parentToken: string, token: string, now?: number): VerifySubTokenResult;
/**
 * Resolve the effective scope for an incoming Authorization Bearer string.
 *
 * Returns:
 *   - "full" when the presented token equals the parent token byte-for-byte
 *     (the founder, full session control).
 *   - the sub-token's scope when the presented token verifies as a sub-token.
 *   - undefined when the presented token is unrecognised / expired / forged
 *     / revoked (when `isRevoked(jti)` returns true).
 *
 * The `now` injection mirrors `verifySubToken` for testability. The optional
 * `isRevoked` callback lets the host plug in its revocation store (e.g.
 * the http-server's in-memory `revokedJtis` Set) without coupling the SDK
 * to any persistence layer.
 */
export declare function resolveAuthScope(parentToken: string, presentedToken: string, now?: number, isRevoked?: (jti: string) => boolean): SubTokenScope | undefined;
/** Options for `rebindSubToken`. */
export interface RebindSubTokenOptions {
    /** Parent session token (same one that minted `oldSubToken`). */
    parentToken: string;
    /** Currently-valid bearer sub-token to upgrade. MUST NOT already carry cnf. */
    oldSubToken: string;
    /** Device public key (JWK). Its thumbprint will be bound into `cnf.jkt`. */
    jwk: EcP256Jwk;
    /** Optional clock injection (defaults to `Date.now()`). */
    now?: () => number;
}
/** Result of `rebindSubToken`. */
export interface RebindSubTokenResult {
    /** New sender-constrained sub-token. */
    subToken: string;
    /** Unix epoch ms for the new expiry (same as old — TTL inherited). */
    expiresAt: number;
    /** Scope (carried over from the old sub-token). */
    scope: SubTokenScope;
    /** jti of the OLD sub-token. The caller MUST add it to the revocation list. */
    oldJti: string;
    /** jti of the NEW sub-token (for log correlation). */
    newJti: string;
}
/**
 * Rebind a bearer sub-token to a device keypair via RFC 7800 `cnf` claim.
 *
 * Used by the PWA after pairing : the bearer sub-token from the QR is
 * upgraded to a sender-constrained token bound to a P-256 key generated
 * non-extractably in IndexedDB. The new token's TTL + scope are inherited
 * from the old one — the caller is responsible for revoking `oldJti`
 * server-side immediately after a successful rebind.
 *
 * @throws Error with a `code` property on validation failure :
 *   - `parent_missing`     — parentToken empty
 *   - `old_malformed`      — oldSubToken not a `<payload>.<sig>` pair
 *   - `old_invalid`        — HMAC fails, payload decode fails, etc
 *   - `old_expired`        — exp already passed
 *   - `already_bound`      — oldSubToken already carries cnf claim
 *   - `jwk_invalid`        — wrong kty/crv or missing x/y
 *   - `jwk_private`        — JWK carries `d` (private scalar)
 *
 * @public @since 2.26.0 — P1b-2 PWA DPoP handshake
 */
export declare function rebindSubToken(opts: RebindSubTokenOptions): RebindSubTokenResult;
//# sourceMappingURL=sub-token.d.ts.map