import { type Identity, type DidDocument } from "./identity.js"; import type { Sphere } from "./did.js"; import type { Revocation, VerifyResult } from "./mandate.js"; export declare const SPONSORSHIP_MANDATE_VERSION_CURRENT: "0.1.0"; export declare const CONSUMPTION_RECEIPT_VERSION_CURRENT: "0.1.0"; export type SponsorshipMandateVersion = "0.1.0"; export type ConsumptionReceiptVersion = "0.1.0"; export type AudienceSet = "open" | "list"; export interface SponsorshipAudience { app_did: string; audience_set: AudienceSet; /** REQUIRED iff `audience_set === "list"`. Each entry is a consumer DID. */ consumers?: readonly string[]; } /** * Budget object — see §13.3.4. All caps are denominated in `unit`. A `null` * cap means "no cap of this kind"; the authority falls through to the next * constraint. */ export interface SponsorshipBudget { unit: string; per_user_cap: number; /** If non-null, `per_user_cap` is enforced over this sliding window. */ per_user_window_seconds: number | null; per_day_total_cap: number; /** If non-null, lifetime cap on total sponsored consumption across all consumers. */ pool_cap_total: number | null; } export interface AccountingAuthority { did: string; endpoint: string; } export interface SponsorshipMandate { "aithos-sponsorship-mandate": SponsorshipMandateVersion; id: string; issuer: string; issued_by_key: string; audience: SponsorshipAudience; scopes: readonly string[]; allowed_methods: readonly string[]; allowed_models?: readonly string[]; budget: SponsorshipBudget; accounting_authority: AccountingAuthority; not_before: string; not_after: string; issued_at: string; nonce: string; signature: { alg: "ed25519"; key: string; value: string; }; } export interface CreateSponsorshipMandateArgs { /** Sponsor identity. Signs the mandate. */ issuer: Identity; /** Which sphere key signs. Conventionally `"public"` since the mandate is publicly resolvable. */ sphere?: Sphere; audience: SponsorshipAudience; scopes: readonly string[]; allowedMethods: readonly string[]; allowedModels?: readonly string[]; budget: SponsorshipBudget; accountingAuthority: AccountingAuthority; /** Defaults to now. */ notBefore?: Date; ttlSeconds: number; } export declare function createSponsorshipMandate(args: CreateSponsorshipMandateArgs): SponsorshipMandate; export interface VerifySponsorshipMandateOptions { /** Defaults to `new Date()`. */ now?: Date; } /** * Verify a sponsorship mandate's structure, signature, and time window against * the sponsor's DID document. Does NOT consult a revocation list — caller * provides that separately, exactly as for action mandates (§4.7). */ export declare function verifySponsorshipMandate(mandate: SponsorshipMandate, sponsorDidDoc: DidDocument, options?: VerifySponsorshipMandateOptions): VerifyResult; /** * Canonical hash of a sponsorship mandate, with `signature.value` cleared. * * This is the value an envelope's `sponsorship.hash` and a receipt's * `sponsorship_hash` MUST commit to. Tampering with any byte of the mandate * shifts the hash and breaks subsequent verification. */ export declare function sponsorshipMandateHash(mandate: SponsorshipMandate): string; export type FundedBy = "sponsored" | "purchase" | "grant"; /** * Optional snapshot of relevant counters after the debit. Useful for clients * that want to display remaining quota without a separate roundtrip. * * `user_consumed_window`, `user_cap_remaining`, and `pool_consumed_lifetime` * MAY be null when the relevant constraint is not applicable (e.g. no window * configured, or fallback receipt with no sponsorship in scope). */ export interface LedgerAfter { user_consumed_lifetime: number; user_consumed_window: number | null; user_cap_remaining: number | null; pool_consumed_lifetime: number | null; pool_consumed_today: number; } export interface ConsumptionReceipt { "aithos-consumption-receipt": ConsumptionReceiptVersion; id: string; /** Populated iff funded_by === "sponsored". */ sponsorship_id: string | null; sponsorship_hash: string | null; sponsor_did: string | null; consumer_did: string; app_did: string; method: string; envelope_nonce: string; envelope_hash: string; funded_by: FundedBy; amount: number; unit: string; ledger_after?: LedgerAfter; timestamp: string; issued_by: string; issued_by_key: string; signature: { alg: "ed25519"; key: string; value: string; }; } export interface CreateConsumptionReceiptArgs { /** The authority's own identity. Signs the receipt. */ authority: Identity; /** Which sphere of the authority signs. Defaults to `"public"`. */ authoritySphere?: Sphere; sponsorshipId: string | null; sponsorshipHash: string | null; sponsorDid: string | null; consumerDid: string; appDid: string; method: string; envelopeNonce: string; envelopeHash: string; fundedBy: FundedBy; amount: number; unit: string; ledgerAfter?: LedgerAfter; timestamp?: Date; } export declare function createConsumptionReceipt(args: CreateConsumptionReceiptArgs): ConsumptionReceipt; /** * Verify a consumption receipt against the authority's DID document. * * The verifier checks: schema, version, internal consistency (sponsored vs * fallback fields), `issued_by`/`issued_by_key` match, and the Ed25519 * signature against the authority's published public key. * * It does NOT cross-check against the original sponsorship mandate (that * requires fetching the mandate via §13.3.6 URLs). Callers that need full * end-to-end verification SHOULD fetch the mandate by `sponsorship_id`, verify * the mandate per `verifySponsorshipMandate`, recompute its hash via * `sponsorshipMandateHash`, and compare to `receipt.sponsorship_hash`. */ export declare function verifyConsumptionReceipt(receipt: ConsumptionReceipt, authorityDidDoc: DidDocument): VerifyResult; /** * Usage snapshot the authority MUST gather from its ledger before deciding * whether a sponsorship covers a call. All counters are in the mandate's * `budget.unit`. */ export interface SponsorshipUsageSnapshot { consumerConsumedLifetime: number; consumerConsumedWindow?: number; consumerWindowStartedAt?: Date; poolConsumedToday: number; poolConsumedLifetime: number; sponsorWalletBalance: number; } export type EligibilityReason = "ok" | "expired" | "not_yet_valid" | "method_blocked" | "model_blocked" | "audience_excluded" | "per_user_cap_reached" | "per_user_window_cap_reached" | "per_day_cap_reached" | "pool_cap_reached" | "wallet_insufficient"; export interface EligibilityInput { mandate: SponsorshipMandate; consumerDid: string; method: string; /** Required iff the mandate carries `allowed_models`. */ model?: string; estimatedAmount: number; usage: SponsorshipUsageSnapshot; /** Defaults to `new Date()`. */ now?: Date; } export interface EligibilityDecision { ok: boolean; reason: EligibilityReason; } /** * Decide whether a sponsorship mandate can cover a given operation under the * current usage snapshot. Pure function — no side effects, no I/O. * * The authority calls this AFTER having verified the mandate's signature and * checked that it is not revoked. This function does not re-verify the * mandate; it only applies the scope and budget rules. * * Checks are evaluated in the order specified in §13.7 of the draft. The * first failing check determines the `reason`. */ export declare function evaluateEligibility(input: EligibilityInput): EligibilityDecision; export interface RevokeSponsorshipArgs { /** Sponsor identity — must match `mandate.issuer`. */ issuer: Identity; mandate: SponsorshipMandate; reason: string; revokedAt?: Date; } /** * Build a §4.6-style revocation document targeting a sponsorship mandate. * * The output reuses the existing `Revocation` shape with `mandate_kind` set * to `"sponsorship-mandate"`. Authorities MUST consult this list before * authorizing a sponsored debit (§13.9). */ export declare function createSponsorshipRevocation(args: RevokeSponsorshipArgs): Revocation;