FabricFabricHarness
Databricks

Local naming and authentication

Understand Fabric job, agent, App, tenant, and Databricks principal names while developing locally.

Local development involves two independent authentication boundaries and several intentionally separate names. Keeping them separate prevents a local API token from being mistaken for a Databricks credential, or a model endpoint name from becoming an agent route.

The complete local request

Diagram flow: fh run; HTTP client; Fabric ingress authentication; Fabric principal and tenant; Job or agent route; Session and policy; Developer PAT; OAuth M2M service principal; On-behalf-of user; AI Gateway or Model Serving; SQL, Unity Catalog, AI Search; Lakebase and Volumes.
Text alternative and Mermaid source

Diagram flow: fh run; HTTP client; Fabric ingress authentication; Fabric principal and tenant; Job or agent route; Session and policy; Developer PAT; OAuth M2M service principal; On-behalf-of user; AI Gateway or Model Serving; SQL, Unity Catalog, AI Search; Lakebase and Volumes.

flowchart LR
  subgraph Caller[Local caller]
    CLI[fh run]
    HTTP[HTTP client]
  end

  subgraph Fabric[Local Fabric runtime]
    INGRESS[Fabric ingress authentication]
    PRINCIPAL[Fabric principal and tenant]
    ROUTE[Job or agent route]
    SESSION[Session and policy]
  end

  subgraph DatabricksAuth[Databricks authentication]
    PAT[Developer PAT]
    M2M[OAuth M2M service principal]
    OBO[On-behalf-of user]
  end

  subgraph Databricks[Databricks workspace]
    MODEL[AI Gateway or Model Serving]
    DATA[SQL, Unity Catalog, AI Search]
    STATE[Lakebase and Volumes]
  end

  CLI -->|trusted local process| ROUTE
  HTTP -->|Bearer token or local default| INGRESS
  INGRESS --> PRINCIPAL
  PRINCIPAL --> ROUTE
  ROUTE --> SESSION
  SESSION --> PAT
  SESSION --> M2M
  SESSION --> OBO
  PAT --> MODEL
  PAT --> DATA
  M2M --> MODEL
  M2M --> DATA
  M2M --> STATE
  OBO --> DATA

  classDef local fill:#f4f4f5,stroke:#71717a,color:#18181b
  classDef fabric fill:#dbeafe,stroke:#2563eb,color:#172554
  classDef identity fill:#fef3c7,stroke:#d97706,color:#422006
  classDef dbx fill:#dcfce7,stroke:#16a34a,color:#052e16
  class CLI,HTTP local
  class INGRESS,PRINCIPAL,ROUTE,SESSION fabric
  class PAT,M2M,OBO identity
  class MODEL,DATA,STATE dbx

The Fabric principal answers who may invoke this local runtime and tenant. The Databricks principal answers who performs the model, data, and state operation in the workspace. They can be the same organizational identity, but they use different credentials and are configured independently.

Naming model

Diagram flow: Project metadata.name leads to Declarative Automation Bundle name; run.idPrefix leads to Databricks App resource name; .fabricharness/jobs/report.ts leads to Declared job name or report; .fabricharness/agents/support.ts leads to Persistent route support; AGENTNAME leads to Caller instance customer-42; INSTANCE leads to Named session default; DATABRICKS_MODEL leads to AI Gateway model service.
Text alternative and Mermaid source

Diagram flow: Project metadata.name leads to Declarative Automation Bundle name; run.idPrefix leads to Databricks App resource name; .fabricharness/jobs/report.ts leads to Declared job name or report; .fabricharness/agents/support.ts leads to Persistent route support; AGENTNAME leads to Caller instance customer-42; INSTANCE leads to Named session default; DATABRICKS_MODEL leads to AI Gateway model service.

flowchart TB
  PROJECT[Project metadata.name] --> BUNDLE[Declarative Automation Bundle name]
  PREFIX[run.idPrefix] --> APP[Databricks App resource name]
  JOBFILE[.fabricharness/jobs/report.ts] --> JOBNAME[Declared job name or report]
  AGENTFILE[.fabricharness/agents/support.ts] --> AGENTNAME[Persistent route support]
  AGENTNAME --> INSTANCE[Caller instance customer-42]
  INSTANCE --> NAMEDSESSION[Named session default]
  MODELENV[DATABRICKS_MODEL] --> MODELSERVICE[AI Gateway model service]

  classDef source fill:#f4f4f5,stroke:#71717a,color:#18181b
  classDef fabric fill:#dbeafe,stroke:#2563eb,color:#172554
  classDef dbx fill:#dcfce7,stroke:#16a34a,color:#052e16
  class PROJECT,PREFIX,JOBFILE,AGENTFILE,MODELENV source
  class BUNDLE,APP,JOBNAME,AGENTNAME,INSTANCE,NAMEDSESSION fabric
  class MODELSERVICE dbx
NameSourceExampleUsed for
Bundle namemetadata.nameanalytics-agentsDatabricks Declarative Automation Bundle identity
App namerun.idPrefixanalytics-appDatabricks App resource
Job nameDefinition name, then relative file pathdaily-reportCLI and /jobs/:name
Persistent agent nameRelative path under agents/support/agents/:name/:instanceId
Instance IDSupplied by the callercustomer-42Addressable persistent agent instance
Session nameSupplied by the caller; default defaultincident-104Conversation within an instance
Databricks modelDATABRICKS_MODEL or definition modelsystem.ai.gpt-oss-20bAI Gateway or serving endpoint selection

Configure the project and App names in .fabricharness/config.ts:

.fabricharness/config.ts
export default {
  metadata: { name: 'analytics-agents' },
  run: {
    idPrefix: 'analytics-app',
    target: 'node',
  },
};

metadata.name and run.idPrefix are sanitized for Databricks resource naming. They do not rename jobs, persistent agents, Unity Catalog objects, or model endpoints.

Finite job names

For finite jobs, a declared name is authoritative:

.fabricharness/jobs/report.ts
import { defineAgent, schema } from '@fabric-harness/sdk';

export default defineAgent({
  name: 'daily-report',
  input: schema.object({ accountId: schema.string() }),
  output: schema.string(),
  model: 'databricks/system.ai.gpt-oss-20b',
  triggers: { manual: true, webhook: true },
  run: async ({ init, input }) => {
    const session = await (await init()).session();
    return session.prompt(`Summarize account ${input.accountId}.`);
  },
});
fh run daily-report --account-id account-42
POST /jobs/daily-report

If name is omitted, jobs/report.ts becomes report. Nested paths remain namespaced: jobs/operations/daily.ts becomes operations/daily.

Persistent agent names

Persistent routes derive from the relative path under .fabricharness/agents/:

.fabricharness/agents/support.ts
import { createAgent } from '@fabric-harness/sdk';

export default createAgent(({ id }) => ({
  model: 'databricks/system.ai.gpt-oss-20b',
  instructions: `Support account ${id} using governed workspace data.`,
  triggers: { webhook: true },
}));

The route below addresses agent support, instance customer-42, session incident-104:

curl -X POST \
  'http://localhost:3000/agents/support/customer-42?wait=false' \
  -H 'content-type: application/json' \
  -d '{"message":"Investigate the failed pipeline.","session":"incident-104"}'

Fabric persists that identity as the tuple (support, customer-42, incident-104). The instance and session names are application identifiers, not Databricks users or Unity Catalog principals.

Fabric authentication while local

Direct CLI execution does not cross an HTTP authentication boundary:

fh run daily-report --account-id account-42 --mock

fh dev permits requests without a token in local mode and records them as the synthetic local-development service principal. This is intended only for loopback development.

To exercise the authenticated HTTP path locally, set an API token before starting the server:

export FABRIC_HARNESS_API_TOKEN="$(openssl rand -hex 32)"
fh dev
curl -X POST http://localhost:3000/jobs/daily-report \
  -H "authorization: Bearer $FABRIC_HARNESS_API_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"accountId":"account-42"}'

When FABRIC_ENV=production or NODE_ENV=production, the server fails closed. With no API token or custom authenticate hook, protected requests receive 401. Query-string tokens exist for limited transport compatibility; use the Authorization header for normal clients so tokens do not enter URLs or access logs.

For OIDC, role, permission, and tenant-aware ingress, use a structured server authenticator. See Authentication and RBAC for JWT validation and Databricks Apps presets.

Tenant selection

A structured Fabric principal can be bound to one tenant or an allowlist:

{
  id: 'analyst-123',
  kind: 'user',
  tenantIds: ['customer-42', 'customer-84'],
  permissions: ['agent:invoke', 'session:read'],
  roles: ['analyst'],
}

The caller selects an allowed tenant with X-Fabric-Tenant:

curl -X POST http://localhost:3000/jobs/daily-report \
  -H "authorization: Bearer $FABRIC_HARNESS_API_TOKEN" \
  -H 'x-fabric-tenant: customer-42' \
  -H 'content-type: application/json' \
  -d '{"accountId":"account-42"}'

A single-tenant principal binds implicitly. A multi-tenant allowlist requires an explicit selection, and selecting a tenant outside the allowlist returns 403.

Databricks authentication while local

Fabric runtime credentials come from environment variables or an explicit DatabricksPrincipal. For local development, the recommended path uses the same Databricks CLI OAuth profile for runtime tokens and deployment. Production Databricks Apps continue to use their app service principal and optional on-behalf-of user token.

Databricks CLI OAuth for local development

Authenticate once with the Databricks CLI:

databricks auth login \
  --host https://adb-1234567890123456.7.azuredatabricks.net \
  --profile fabric-harness

Then select that profile for Harness runtime token resolution:

.env.local
DATABRICKS_HOST=https://adb-1234567890123456.7.azuredatabricks.net
DATABRICKS_CONFIG_PROFILE=fabric-harness
DATABRICKS_AUTH_MODE=cli
DATABRICKS_MODEL=system.ai.gpt-oss-20b

For identity/token resolution (databricksIdentity, databricksPrincipalFromEnv), DATABRICKS_CONFIG_PROFILE alone is sufficient — the workspace host is resolved from the named profile in ~/.databrickscfg. The full recipe runtime and bundle config shown above still take an explicit workspace host (DATABRICKS_HOST / host), which application code can obtain from the profile via await databricksHostFromCliProfile(profile).

Profile-only fh deploy --target databricks-serving requires an installed @fabric-harness/databricks version that exports databricksHostFromCliProfile. Older installations remain supported when DATABRICKS_HOST is set explicitly; the CLI fails before building or registering a model when the helper is unavailable.

Token acquisition goes through the official Databricks SDK credential chain: it resolves the named profile, and PAT profiles use the stored token directly. OAuth (CLI) profiles shell out to the Go Databricks CLI (>= 0.100.0) via databricks auth token --profile <name> on every request — no --host argument, so the CLI's own OAuth cache and refresh apply, not Harness's. Harness attempts no fallback login; run databricks auth login first if the CLI reports the profile isn't authenticated. A pasted AI Gateway URL is normalized to the workspace origin before management or authentication URLs are constructed.

Explicit wiring is also available:

import { databricksIdentity } from '@fabric-harness/databricks';

const token = databricksIdentity({
  kind: 'cli-profile',
  profile: 'fabric-harness',
});

host is optional; when set, it takes precedence as the workspace origin for API calls, but CLI token acquisition always follows the named profile — host and the profile must point at the same workspace.

PAT for one developer

Use a personal access token for a controlled, single-user development session:

.env.local
DATABRICKS_HOST=https://adb-1234567890123456.7.azuredatabricks.net
DATABRICKS_TOKEN=dapi...
DATABRICKS_WAREHOUSE_ID=0123456789abcdef
DATABRICKS_MODEL=system.ai.gpt-oss-20b
DATABRICKS_INFERENCE_MODE=auto

The process acts with the PAT owner's Databricks permissions. Do not use a developer PAT as an App or shared production identity. PAT lookup is explicit; DATABRICKS_BEARER is also accepted for headless compatibility with pre-fetched Databricks tokens.

OAuth M2M for production-like development

Use a service principal to reproduce CI and App-style authorization locally:

.env.local
DATABRICKS_HOST=https://adb-1234567890123456.7.azuredatabricks.net
DATABRICKS_CLIENT_ID=00000000-0000-0000-0000-000000000000
DATABRICKS_CLIENT_SECRET=resolve-from-your-secret-manager
DATABRICKS_WAREHOUSE_ID=0123456789abcdef
DATABRICKS_MODEL=system.ai.gpt-oss-20b
DATABRICKS_INFERENCE_MODE=auto

Fabric exchanges the client credentials at the workspace OIDC endpoint, caches the access token, refreshes before expiry, and deduplicates concurrent refreshes. If DATABRICKS_TOKEN and OAuth credentials are both present, the PAT wins; unset DATABRICKS_TOKEN when validating M2M behavior.

Run a live request after loading the environment:

fh run databricks-analyst \
  --question 'Describe main.sales.orders'

Explicit principal wiring

Use an explicit principal when one process needs more than one Databricks identity:

import { databricks } from '@fabric-harness/databricks';

const dbx = databricks({
  host: process.env.DATABRICKS_HOST!,
  principal: {
    kind: 'service-principal',
    host: process.env.DATABRICKS_HOST!,
    clientId: process.env.DATABRICKS_CLIENT_ID!,
    clientSecret: process.env.DATABRICKS_CLIENT_SECRET!,
  },
  model: 'system.ai.gpt-oss-20b',
  warehouseId: process.env.DATABRICKS_WAREHOUSE_ID,
  governance: { catalogs: ['main'] },
});

The token resolver is awaited for each Databricks operation. Raw credentials are not added to model messages, tool inputs, lineage labels, or logs.

OBO for a signed-in user

On-behalf-of authentication preserves a user's Unity Catalog grants:

const principal = {
  kind: 'on-behalf-of' as const,
  userToken: async () => currentUserAccessToken(),
  label: 'signed-in-analyst',
};

Fabric does not manufacture or persist an OBO token. Databricks Apps performs user consent and token refresh, then forwards the current user access token on each request. Generated App artifacts validate it against the workspace current-user API, cache validation only by token digest, and map the verified user to a stable, opaque Fabric tenant with least-privilege permissions.

For a protected diagnostic route, inspectDatabricksAppUserAuthorization() validates the forwarded token against the workspace current-user API and returns only identity plus issued/expiry metadata. It never returns the token or copies workspace error bodies. The reference App exposes this at GET /certification/obo for lifecycle certification.

Diagram flow: autonumber; actor Analyst; participant Ingress as Fabric ingress; participant Runtime as Fabric runtime; participant Provider as Databricks token provider; participant UC as Unity Catalog; Analyst leads to >Ingress: Request with signed user identity; Ingress leads to >Ingress: Validate issuer, audience, expiry, signature; Ingress leads to >Runtime: Fabric user principal and tenant; Runtime leads to >Provider: Resolve short-lived user token; Provider leads to >Runtime: OBO access token; Runtime leads to >UC: Query as signed-in analyst.
Text alternative and Mermaid source

Diagram flow: autonumber; actor Analyst; participant Ingress as Fabric ingress; participant Runtime as Fabric runtime; participant Provider as Databricks token provider; participant UC as Unity Catalog; Analyst leads to >Ingress: Request with signed user identity; Ingress leads to >Ingress: Validate issuer, audience, expiry, signature; Ingress leads to >Runtime: Fabric user principal and tenant; Runtime leads to >Provider: Resolve short-lived user token; Provider leads to >Runtime: OBO access token; Runtime leads to >UC: Query as signed-in analyst.

sequenceDiagram
  autonumber
  actor Analyst
  participant Ingress as Fabric ingress
  participant Runtime as Fabric runtime
  participant Provider as Databricks token provider
  participant UC as Unity Catalog

  Analyst->>Ingress: Request with signed user identity
  Ingress->>Ingress: Validate issuer, audience, expiry, signature
  Ingress->>Runtime: Fabric user principal and tenant
  Runtime->>Provider: Resolve short-lived user token
  Provider-->>Runtime: OBO access token
  Runtime->>UC: Query as signed-in analyst
  UC-->>Runtime: User-specific result or denial
  Runtime-->>Analyst: Audited response

One interactive CLI authorization can certify both initial login and refresh. The prepare phase uses the selected U2M profile and stores only non-secret lifetime metadata. After the first one-hour token expires, verify asks the CLI for a refreshed token, calls the App again, and requires the same user, a newer issue time, and a later expiry:

databricks auth login --host "$DATABRICKS_HOST" --profile fabric-harness

DATABRICKS_APP_URL="$DATABRICKS_APP_URL" \
  DATABRICKS_PROFILE=fabric-harness \
  node scripts/certify-databricks-obo-lifecycle.mjs prepare

# Run after the expiry printed by prepare.
DATABRICKS_APP_URL="$DATABRICKS_APP_URL" \
  DATABRICKS_PROFILE=fabric-harness \
  node scripts/certify-databricks-obo-lifecycle.mjs verify

Use certify instead of the two phases when one process can remain active across the token-expiry window. Before sharing the App, run scripts/certify-databricks-app-user-isolation.mjs with two distinct U2M profiles as described in authoring certification.

Runtime credentials versus deployment profiles

DATABRICKS_TOKEN or the OAuth environment variables authenticate calls made by the running agent. A Databricks CLI profile authenticates build deployment commands:

databricks auth login --host "$DATABRICKS_HOST" --profile analytics-dev
fh build --target databricks-app
fh deploy --target databricks-app --profile analytics-dev

Passing --profile does not change the identity used later by the deployed App. Databricks Apps supplies its service-principal identity at runtime; configure App resource permissions and secret references separately.

Local verification checklist

  1. Run fh agents and confirm the job and persistent-agent route names.
  2. Run with --mock before adding workspace credentials.
  3. Set only one Databricks credential mode at a time.
  4. Use OAuth M2M to reproduce unattended CI permissions.
  5. Set FABRIC_HARNESS_API_TOKEN to test authenticated local HTTP calls.
  6. Test one allowed and one denied Unity Catalog object.
  7. Verify the selected Fabric tenant appears in audit, lineage, and cost attribution.
  8. Use fh deploy --preview --target databricks-app --profile analytics-dev before deploying.

Continue with the Databricks quickstart to scaffold an App or Enterprise Databricks controls to configure OIDC, policy, durability, lineage, and cost enforcement.