---
title: "API: Configuration, tenancy and themes"
description: "Public TypeScript declarations verified against the package export entries."
---

> Documentation Index
> Fetch the complete documentation index at: https://pupitre.rikology.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API: Configuration, tenancy and themes

This reference is generated before development, checking and building from declarations reachable through the package's published entry points, following re-exports. Internal source-module exports are omitted. Types shown together share their source module; follow the source link for imports and implementation. Use the guides for examples and runtime behavior.

## src/define_config.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/define_config.ts)

```ts
/** Attribute names (model properties, not DB columns) read on the host user model. */
export type AuthColumns = {
    name: string;
    email: string;
    password: string;
    avatar: string;
    emailVerifiedAt: string;
    timezone: string;
    locale: string;
    currentTenantId: string;
    mfaSecret: string;
    mfaRecoveryCodes: string;
    mfaEnabledAt: string;
    socialProvider?: string;
    socialProviderId?: string;
};

export interface PupitreConfig {
    cache: {
        dev: boolean;
    };
    timezone: string;
    i18n?: {
        fallbackLocale?: string;
        messages?: LocaleMessages;
    };
    storage: {
        disk: string;
    };
    policies?: () => Promise<Record<string, unknown>>;
    auth: {
        /** Host user model used by panel auth pages; required when any auth page is enabled. */
        model?: () => Promise<{
            default: LucidModel;
        }>;
        columns: AuthColumns;
        /** Password reset token lifetime in minutes. */
        passwordResetTtl: number;
        passkeys?: {
            rpId?: string;
            origin?: string;
            rpName?: string;
        };
    };
    mail?: Lazy<MailAdapter>;
    notifications?: Lazy<NotificationsAdapter>;
    jobs?: Lazy<JobsAdapter>;
    audit?: Lazy<AuditAdapter>;
    realtime?: Lazy<RealtimeAdapter>;
    exports?: {
        maxSyncRows?: number;
        maxAsyncRows?: number;
        retentionMinutes?: number;
    };
}

export type PupitreConfigInput = Partial<Pick<PupitreConfig, 'timezone' | 'policies' | 'mail' | 'notifications' | 'jobs' | 'exports' | 'audit' | 'i18n'>> & {
    realtime?: PupitreConfig['realtime'];
    cache?: Partial<PupitreConfig['cache']>;
    storage?: Partial<PupitreConfig['storage']>;
    auth?: Partial<Omit<PupitreConfig['auth'], 'columns'>> & {
        columns?: Partial<AuthColumns>;
    };
};

/** Creates the resolved Pupitre configuration with stable defaults. */
export function defineConfig(config: PupitreConfigInput = {});

/** Resolves a lazily configured adapter (`value` or `() => Promise<value>`). */
export async function resolveAdapter<T extends object>(value: Lazy<T> | undefined): Promise<T | undefined>;
```

## src/definition/tenancy.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/definition/tenancy.ts)

```ts
/** Serializable tenant identity exposed to controllers and the client. */
export type TenantSummary = {
    id: string | number;
    name: string;
    slug?: string;
};

/** Resolved tenant for the current request. `record` is the host model instance when available. */
export type ResolvedTenant = TenantSummary & {
    record?: LucidRow | object;
};

/**
 * Tenancy adapter (DESIGN §Adapters). Implementations: default column driver
 * (`src/tenancy/column_driver.ts`) and `@rikology/pupitre/drivers/tenant`.
 */
export interface TenancyAdapter {
    /** Resolves the tenant for the request; `null` means "not found / not a member". */
    resolve(ctx: HttpContext): Promise<ResolvedTenant | null>;
    /** Restricts a resource query to the tenant. */
    scope(query: ModelQueryBuilderContract<LucidModel>, tenant: ResolvedTenant, options: {
        ownership: string;
        model: LucidModel;
        ctx: HttpContext;
    }): void;
    /** Fills ownership on create data (before persistence). */
    fill(data: Record<string, unknown>, tenant: ResolvedTenant, options: {
        ownership: string;
        model: LucidModel;
        ctx: HttpContext;
    }): Record<string, unknown>;
    /** Tenants the user can switch to. */
    listForUser(ctx: HttpContext): Promise<TenantSummary[]>;
    /** Implicit mode only: persists the user's current tenant. */
    switch?(ctx: HttpContext, tenantId: string | number): Promise<boolean>;
    /** Optional execution context wrapper used by tenant-aware optional peers. */
    run?(tenant: ResolvedTenant, next: () => Promise<void>): Promise<void>;
}

export type TenancyMode = 'url' | 'implicit';

export type TenancyDefinition = {
    mode: TenancyMode;
    /** Lazy tenant model (default column driver). */
    model?: () => Promise<{
        default: LucidModel;
    }>;
    /** Ownership column on resource tables; default `tenant_id`. */
    ownership: string;
    /** Tenant attribute used in URLs (mode `url`); default `slug`. */
    slugAttribute: string;
    /** Tenant attribute used as display name; default `name`. */
    titleAttribute: string;
    /** Show the tenant switcher. */
    menu: boolean;
    /** Custom adapter; defaults to the column driver. */
    adapter?: TenancyAdapter | (() => Promise<TenancyAdapter>);
    /** Default column driver: tenants the user belongs to. */
    tenantsForUser?: (user: LucidRow, ctx: HttpContext) => Promise<readonly LucidRow[]>;
    /** Implicit mode: user attribute holding the current tenant id; default `currentTenantId`. */
    currentTenantAttribute: string;
};
```

## src/definition/theme.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/definition/theme.ts)

```ts
export type ThemeDefinition = {
    font?: {
        family: string;
        url?: string;
    };
    density?: 'compact' | 'comfortable';
    sidebar?: 'expanded' | 'collapsed' | 'top-nav';
    sidebarCollapsibleOnDesktop?: boolean;
    contentWidth?: 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | '6xl' | '7xl' | 'full' | 'screen';
    colors?: Record<string, string>;
    radius?: string;
    vars?: Record<string, string>;
    stylesheet?: string;
    darkMode?: 'light' | 'dark' | 'system';
    /** Legacy primary color accepted by panel.theme(). */
    primary?: string;
};

/** Creates a validated, immutable-friendly panel theme definition. */
export function defineTheme<T extends ThemeDefinition>(theme: T): T;
```

## src/adapters/mail.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/adapters/mail.ts)

```ts
/** Outgoing email produced by Pupitre (password reset, verification). */
export type PupitreMailMessage = {
    to: string;
    subject: string;
    html: string;
    text: string;
};

/**
 * Mail adapter (DESIGN §Adapters). Official driver over `@adonisjs/mail`:
 * `@rikology/pupitre/drivers/mail`.
 */
export interface MailAdapter {
    send(message: PupitreMailMessage): Promise<void>;
}
```

## src/adapters/jobs.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/adapters/jobs.ts)

```ts
/** Jobs integration used by asynchronous actions and exports. */
export interface JobsAdapter {
    dispatch(job: string, payload: Record<string, unknown>): Promise<void>;
    register(job: string, handler: (payload: Record<string, unknown>) => Promise<void>): void;
    initialize?(): Promise<void>;
}
```

## src/adapters/notifications.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/adapters/notifications.ts)

```ts
/** A database notification as shown in the panel tray. */
export type PupitreNotificationItem = {
    id: string;
    type: 'success' | 'error' | 'warning' | 'info';
    title: string;
    body?: string;
    url?: string;
    actions?: NotificationAction[];
    readAt: string | null;
    createdAt: string;
};

export type PupitreNotificationMessage = Omit<PupitreNotificationItem, 'id' | 'readAt' | 'createdAt'>;

/**
 * Notifications adapter (DESIGN §Adapters). Official driver over
 * `@rikology/adonisjs-notifications`: `@rikology/pupitre/drivers/notifications`.
 * Without an adapter only flash messages work.
 */
export interface NotificationsAdapter {
    toInbox(user: LucidRow, message: PupitreNotificationMessage, ctx?: HttpContext): Promise<void>;
    inbox(user: LucidRow, options: {
        page: number;
        perPage: number;
    }, ctx?: HttpContext): Promise<{
        items: PupitreNotificationItem[];
        meta: {
            total: number;
            perPage: number;
            currentPage: number;
            lastPage: number;
        };
    }>;
    unreadCount(user: LucidRow, ctx?: HttpContext): Promise<number>;
    markRead(user: LucidRow, ids: string[] | 'all', ctx?: HttpContext): Promise<void>;
}
```

## src/adapters/realtime.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/adapters/realtime.ts)

```ts
/** Transport-neutral ping publisher. Channel authorization belongs to the adapter. */
export interface RealtimeAdapter {
    driver: string;
    publish(channel: string, event: RealtimeEvent): Promise<void>;
    initialize?(manager: PupitreManager): void | Promise<void>;
}
```

## src/adapters/audit.ts

[Source](https://github.com/rikoriswandha/pupitre/blob/main/packages/pupitre/src/adapters/audit.ts)

```ts
export type AuditChange = {
    field: string;
    old: unknown;
    new: unknown;
};

export type AuditEntry = {
    id: string;
    event: string;
    auditableType: string | null;
    auditableId: string | null;
    actor: {
        type: string;
        id: string | null;
        label?: string;
    } | null;
    changes: AuditChange[];
    tags: string[];
    createdAt: string;
};

export type AuditPagination = {
    page?: number;
    perPage?: number;
};

export type AuditRecordOptions = AuditPagination & {
    tenantId?: string;
};

export type AuditPage = {
    items: AuditEntry[];
    meta: {
        total: number;
        perPage: number;
        currentPage: number;
        lastPage: number;
    };
};

/** Hosts own audit capture, storage, retention and redaction. Pupitre only reads it. */
export interface AuditAdapter {
    forRecord(model: LucidModel, id: string | number, options?: AuditRecordOptions): Promise<AuditPage>;
}
```

Source: https://pupitre.rikology.com/reference/api-configuration/index.mdx
