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
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
| Name | Source | Example | Used for |
|---|---|---|---|
| Bundle name | metadata.name | analytics-agents | Databricks Asset 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 { agent, schema } from '@fabric-harness/sdk';
export default agent({
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-20bHarness invokes databricks auth token --host ... --profile ... --output json, caches only the
returned access token, and refreshes it every 30 minutes with --force-refresh. If the CLI cache is
stale, it attempts databricks auth login --no-browser and retries. 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',
host: process.env.DATABRICKS_HOST!,
profile: 'fabric-harness',
});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. databricksApp().onBehalfOf()
maps that request-scoped token to the Fabric actor and Unity Catalog principal.
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.
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 verifyRuntime 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.