/**
 * Authoritative ownership index for immutable-keyed assets.
 *
 * Assets are stored under an immutable key `{assetId}/{filename}` (assetId =
 * UUIDv4), so the storage location no longer encodes the owning entity's
 * mutable `(type, id)`. This table is the single source of truth for:
 *   - per-repo authorization on the serving route (`repo`), and
 *   - "which assets does entity E own?" for delete/transfer (`owner_type`,
 *     `owner_id`).
 *
 * It is written authoritatively at upload and updated by every mutation path
 * (rename/changeType/move/transfer/delete). It is NOT rebuilt from entity
 * bodies: deriving ownership from user-editable `/api/assets/{id}` embeds would
 * let a user in repo A claim an asset owned by repo B by pasting its URL —
 * a privilege-escalation vector. Treat this db as durable critical state
 * (it lives on the same persistent workspace volume as auth.db / secrets.db),
 * not a regenerable cache.
 *
 * `migration_ledger` is a transient table used only by the one-time
 * old→new key migration to stay idempotent; it is dropped after cutover.
 */
export interface AssetIndexRow {
    assetId: string;
    repo: string;
    ownerType: string;
    ownerId: string;
    filename: string;
    contentType: string;
    mediaType: string;
    name: string | null;
    createdAt: string;
}
export type AssetIndexInput = Omit<AssetIndexRow, 'createdAt'> & {
    createdAt?: string;
};
/** Owner-reassignment changes for a mutation. Any subset may be supplied. */
export interface OwnerReassignment {
    ownerType?: string;
    ownerId?: string;
    repo?: string;
}
export declare class AssetIndexService {
    private db;
    constructor(workspacePath: string);
    /** Keep assets.db (+ WAL/SHM) out of the workspace config repo — it's
     *  local, durable on the workspace volume, and not safely rebuildable from
     *  bodies, so it's never committed. Mirrors auth.db / secrets.db. */
    private ensureGitignore;
    /** Insert or replace the row for an assetId (idempotent on re-run). */
    put(input: AssetIndexInput): void;
    get(assetId: string): AssetIndexRow | null;
    /**
     * Assets owned by `(repo, ownerType, ownerId)`. The `repo` scope is
     * mandatory: entity ids are unique only within (repo, type), NOT across the
     * workspace — `note/foo` can exist in two repos. Matching on owner alone
     * would let a delete/rename in repo A touch repo B's identically-named
     * entity's assets (cross-repo data loss).
     */
    listByOwner(repo: string, ownerType: string, ownerId: string): AssetIndexRow[];
    listByRepo(repo: string): AssetIndexRow[];
    /**
     * Reassign every asset owned by `(repo, fromType, fromId)`. Used by mutations:
     *   - rename:      reassignOwner(repo, type, oldId, { ownerId: newId })
     *   - changeType:  reassignOwner(repo, oldType, id, { ownerType: newType })
     *   - cross-repo move/transfer: reassignOwner(sourceRepo, type, id, { repo: target })
     * `repo` scopes the match (see listByOwner) — without it a rename in one repo
     * would reassign a same-named entity's assets in another. Returns rows updated.
     * No blob movement — keys are immutable.
     */
    reassignOwner(repo: string, fromType: string, fromId: string, changes: OwnerReassignment): number;
    delete(assetId: string): void;
    ledgerGet(repo: string, oldKey: string): string | null;
    ledgerPut(repo: string, oldKey: string, assetId: string): void;
    ledgerEntries(): Array<{
        repo: string;
        oldKey: string;
        assetId: string;
    }>;
    dropLedger(): void;
    close(): void;
    private toRow;
}
