Configuration

Learn about backfill.config.ts permissions, settings defaults, page declarations, UI contributions.

The manifest is one file: backfill.config.ts. It’s a TypeScript module whose default export is the result of defineExtension({...}). The platform reads and validates it before running any of your code.

Keep declarations that can grow large out of the manifest. Custom entities live in src/entities/ and custom fields live in src/fields/. The CLI discovers those files and normalizes them into the deployment artifact.

Minimum manifest

import { defineExtension } from "@backfill-io/sdk";

export default defineExtension({
  key: "my-extension",
  name: "My Extension",
  version: "0.1.0",
  permissions: {
    entities: { Invoice: ["read", "write"] },
  },
});

key and name are required. version is recommended. Everything else is optional.

All manifest fields

defineExtension({
  key: string,            // unique extension key
  name: string,           // human display name
  version?: string,
  permissions?: {
    entities?: Record<EntityName, ("read" | "write")[]>,
    http?: string[],      // each must be an https URL with an explicit host
    secrets?: Record<SecretName, ("read" | "write")[]>,
  },
  config?: Record<string, any>,  // settings defaults — see Settings article
  pages?: ExtensionPageConfig[],
  ui?: {
    tabs?: UiTabConfig[],
    panels?: UiPanelConfig[],
    actions?: UiActionConfig[],
  },
});

Permissions are enforced

If your code calls Invoice.update(...) and the manifest only granted read, the runtime rejects the write. Same for HTTP and secrets. There’s no allow-by-default — anything not in the manifest is denied.

entities

permissions: {
  entities: {
    Invoice: ["read", "write"],
    Customer: ["read"],
    NexusAddress: ["read", "write"],   // also for custom entities you declare
  },
}

http

permissions: {
  http: ["https://api.stripe.com/*"],
}
  • Must be https://. Plain HTTP is rejected.
  • Must have an explicit host. * as the host is rejected.
  • Path wildcards are fine.

secrets

permissions: {
  secrets: {
    stripe_api_key: ["read"],
    webhook_secret: ["read"],
  },
}

Names must match ^[a-z][a-z0-9_]{0,62}$. See Secrets.

config

config: {
  taxRate: 8.25,
  taxLabel: "Sales Tax",
  enableTaxOnInvoices: true,
}

Settings.getAll() at runtime returns the merged map of these defaults plus any overrides the workspace admin set in the dashboard.

pages

Each entry registers an extension page. See UI Pages.

pages: [
  {
    id: "nexus-addresses",
    title: "Nexus Addresses",
    path: "nexus-addresses",
    render: "src/pages/nexus-addresses.tsx",
    kind: "list",
    nav: { label: "Nexus Addresses", section: "Tax", order: 10 },
  },
],

ui: tabs, panels, actions

Contribute UI to existing entity pages. See UI Contributions.

ui: {
  actions: [
    {
      id: "save-nexus-address",
      label: "Save Nexus Address",
      page: "nexus-address-form",
      run: "src/pages/actions/save-nexus-address.ts",
    },
  ],
}