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
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 dbxThe 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
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| Name | Source | Example | Used for |
|---|---|---|---|
| Bundle name | metadata.name | analytics-agents | Databricks Declarative Automation Bundle identity |
| App name | run.idPrefix | analytics-app | Databricks App resource |
| Job name | Definition name, then relative file path | daily-report | CLI and /jobs/:name |
| Persistent agent name | Relative path under agents/ | support | /agents/:name/:instanceId |
| Instance ID | Supplied by the caller | customer-42 | Addressable persistent agent instance |
| Session name | Supplied by the caller; default default | incident-104 | Conversation within an instance |
| Databricks model | DATABRICKS_MODEL or definition model | system.ai.gpt-oss-20b | AI Gateway or serving endpoint selection |
Configure the project and App names in .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:
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-42POST /jobs/daily-reportIf 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/:
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 --mockfh 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 devcurl -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-harnessThen select that profile for Harness runtime token resolution:
DATABRICKS_HOST=https://adb-1234567890123456.7.azuredatabricks.net
DATABRICKS_CONFIG_PROFILE=fabric-harness
DATABRICKS_AUTH_MODE=cli
DATABRICKS_MODEL=system.ai.gpt-oss-20bFor 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:
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=autoThe 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:
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=autoFabric 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.
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 responseOne 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 verifyUse 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-devPassing --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
- Run
fh agentsand confirm the job and persistent-agent route names. - Run with
--mockbefore adding workspace credentials. - Set only one Databricks credential mode at a time.
- Use OAuth M2M to reproduce unattended CI permissions.
- Set
FABRIC_HARNESS_API_TOKENto test authenticated local HTTP calls. - Test one allowed and one denied Unity Catalog object.
- Verify the selected Fabric tenant appears in audit, lineage, and cost attribution.
- Use
fh deploy --preview --target databricks-app --profile analytics-devbefore deploying.
Continue with the Databricks quickstart to scaffold an App or Enterprise Databricks controls to configure OIDC, policy, durability, lineage, and cost enforcement.
Databricks quickstart
Install, scaffold, mock, connect, test, build, and deploy a governed Databricks agent with Fabric Harness.
Databricks recipes (`fh add`)
Scaffold Genie analytics copilots, Lakebase, SQL, AI Search, Lakeflow, Jobs, cost controls, and Apps wiring with managed Fabric Harness recipes.