@databricks/appkit 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CLAUDE.md +9 -1
  2. package/dist/appkit/package.js +1 -1
  3. package/dist/cache/index.js.map +1 -1
  4. package/dist/cli/commands/docs.js +7 -1
  5. package/dist/cli/commands/docs.js.map +1 -1
  6. package/dist/cli/commands/generate-types.js +20 -10
  7. package/dist/cli/commands/generate-types.js.map +1 -1
  8. package/dist/cli/commands/lint.js +3 -1
  9. package/dist/cli/commands/lint.js.map +1 -1
  10. package/dist/cli/commands/plugin/add-resource/add-resource.js +73 -8
  11. package/dist/cli/commands/plugin/add-resource/add-resource.js.map +1 -1
  12. package/dist/cli/commands/plugin/create/create.js +164 -20
  13. package/dist/cli/commands/plugin/create/create.js.map +1 -1
  14. package/dist/cli/commands/plugin/create/resource-defaults.js +5 -1
  15. package/dist/cli/commands/plugin/create/resource-defaults.js.map +1 -1
  16. package/dist/cli/commands/plugin/index.js +7 -1
  17. package/dist/cli/commands/plugin/index.js.map +1 -1
  18. package/dist/cli/commands/plugin/list/list.js +7 -1
  19. package/dist/cli/commands/plugin/list/list.js.map +1 -1
  20. package/dist/cli/commands/plugin/sync/sync.js +27 -14
  21. package/dist/cli/commands/plugin/sync/sync.js.map +1 -1
  22. package/dist/cli/commands/plugin/validate/validate.js +39 -9
  23. package/dist/cli/commands/plugin/validate/validate.js.map +1 -1
  24. package/dist/cli/commands/setup.js +6 -5
  25. package/dist/cli/commands/setup.js.map +1 -1
  26. package/dist/connectors/index.js +1 -0
  27. package/dist/connectors/lakebase/index.js.map +1 -1
  28. package/dist/connectors/lakebase-v1/client.js.map +1 -1
  29. package/dist/connectors/vector-search/client.js +9 -0
  30. package/dist/connectors/vector-search/client.js.map +1 -0
  31. package/dist/connectors/vector-search/index.js +3 -0
  32. package/dist/context/execution-context.js +1 -7
  33. package/dist/context/execution-context.js.map +1 -1
  34. package/dist/context/index.js +1 -1
  35. package/dist/context/index.js.map +1 -1
  36. package/dist/index.d.ts +2 -1
  37. package/dist/index.js +2 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/plugin/dev-reader.js.map +1 -1
  40. package/dist/plugins/files/plugin.d.ts +46 -15
  41. package/dist/plugins/files/plugin.d.ts.map +1 -1
  42. package/dist/plugins/files/plugin.js +182 -103
  43. package/dist/plugins/files/plugin.js.map +1 -1
  44. package/dist/plugins/files/policy.d.ts +45 -0
  45. package/dist/plugins/files/policy.d.ts.map +1 -0
  46. package/dist/plugins/files/policy.js +63 -0
  47. package/dist/plugins/files/policy.js.map +1 -0
  48. package/dist/plugins/files/types.d.ts +16 -8
  49. package/dist/plugins/files/types.d.ts.map +1 -1
  50. package/dist/plugins/server/vite-dev-server.js.map +1 -1
  51. package/dist/plugins/serving/serving.d.ts.map +1 -1
  52. package/dist/plugins/serving/serving.js +22 -8
  53. package/dist/plugins/serving/serving.js.map +1 -1
  54. package/dist/plugins/serving/types.d.ts +11 -10
  55. package/dist/plugins/serving/types.d.ts.map +1 -1
  56. package/dist/type-generator/index.js +13 -1
  57. package/dist/type-generator/index.js.map +1 -1
  58. package/dist/type-generator/migration.js +155 -0
  59. package/dist/type-generator/migration.js.map +1 -0
  60. package/dist/type-generator/serving/generator.js +22 -1
  61. package/dist/type-generator/serving/generator.js.map +1 -1
  62. package/dist/type-generator/serving/vite-plugin.d.ts +1 -1
  63. package/dist/type-generator/serving/vite-plugin.js +2 -2
  64. package/dist/type-generator/serving/vite-plugin.js.map +1 -1
  65. package/dist/type-generator/vite-plugin.d.ts.map +1 -1
  66. package/dist/type-generator/vite-plugin.js +3 -4
  67. package/dist/type-generator/vite-plugin.js.map +1 -1
  68. package/docs/api/appkit/Class.PolicyDeniedError.md +52 -0
  69. package/docs/api/appkit/Interface.FilePolicyUser.md +23 -0
  70. package/docs/api/appkit/Interface.FileResource.md +36 -0
  71. package/docs/api/appkit/TypeAlias.FileAction.md +18 -0
  72. package/docs/api/appkit/TypeAlias.FilePolicy.md +20 -0
  73. package/docs/api/appkit/TypeAlias.ServingFactory.md +9 -5
  74. package/docs/api/appkit/Variable.READ_ACTIONS.md +8 -0
  75. package/docs/api/appkit/Variable.WRITE_ACTIONS.md +8 -0
  76. package/docs/api/appkit.md +19 -12
  77. package/docs/development/type-generation.md +6 -5
  78. package/docs/faq.md +8 -8
  79. package/docs/plugins/analytics.md +1 -1
  80. package/docs/plugins/custom-plugins.md +4 -0
  81. package/docs/plugins/execution-context.md +0 -1
  82. package/docs/plugins/files.md +150 -2
  83. package/docs/plugins/{serving.md → model-serving.md} +1 -1
  84. package/docs/plugins/plugin-management.md +22 -6
  85. package/docs/plugins/vector-search.md +247 -0
  86. package/llms.txt +9 -1
  87. package/package.json +1 -1
  88. package/sbom.cdx.json +1 -1
@@ -0,0 +1,36 @@
1
+ # Interface: FileResource
2
+
3
+ Describes the file or directory being acted upon.
4
+
5
+ ## Properties[​](#properties "Direct link to Properties")
6
+
7
+ ### path[​](#path "Direct link to path")
8
+
9
+ ```ts
10
+ path: string;
11
+
12
+ ```
13
+
14
+ Relative path within the volume.
15
+
16
+ ***
17
+
18
+ ### size?[​](#size "Direct link to size?")
19
+
20
+ ```ts
21
+ optional size: number;
22
+
23
+ ```
24
+
25
+ Content length in bytes — only present for uploads.
26
+
27
+ ***
28
+
29
+ ### volume[​](#volume "Direct link to volume")
30
+
31
+ ```ts
32
+ volume: string;
33
+
34
+ ```
35
+
36
+ The volume key (e.g. `"uploads"`).
@@ -0,0 +1,18 @@
1
+ # Type Alias: FileAction
2
+
3
+ ```ts
4
+ type FileAction =
5
+ | "list"
6
+ | "read"
7
+ | "download"
8
+ | "raw"
9
+ | "exists"
10
+ | "metadata"
11
+ | "preview"
12
+ | "upload"
13
+ | "mkdir"
14
+ | "delete";
15
+
16
+ ```
17
+
18
+ Every action the files plugin can perform.
@@ -0,0 +1,20 @@
1
+ # Type Alias: FilePolicy()
2
+
3
+ ```ts
4
+ type FilePolicy = (action: FileAction, resource: FileResource, user: FilePolicyUser) => boolean | Promise<boolean>;
5
+
6
+ ```
7
+
8
+ A policy function that decides whether `user` may perform `action` on `resource`. Return `true` to allow, `false` to deny.
9
+
10
+ ## Parameters[​](#parameters "Direct link to Parameters")
11
+
12
+ | Parameter | Type |
13
+ | ---------- | ----------------------------------------------------------------------- |
14
+ | `action` | [`FileAction`](./docs/api/appkit/TypeAlias.FileAction.md) |
15
+ | `resource` | [`FileResource`](./docs/api/appkit/Interface.FileResource.md) |
16
+ | `user` | [`FilePolicyUser`](./docs/api/appkit/Interface.FilePolicyUser.md) |
17
+
18
+ ## Returns[​](#returns "Direct link to Returns")
19
+
20
+ `boolean` | `Promise`<`boolean`>
@@ -1,15 +1,19 @@
1
1
  # Type Alias: ServingFactory
2
2
 
3
3
  ```ts
4
- type ServingFactory = keyof ServingEndpointRegistry extends never ? (alias?: string) => ServingEndpointHandle : <K>(alias: K) => ServingEndpointHandle<ServingEndpointRegistry[K]["request"], ServingEndpointRegistry[K]["response"]>;
4
+ type ServingFactory = keyof ServingEndpointRegistry extends never ? (alias?: string) => ServingEndpointHandle : true extends IsUnion<keyof ServingEndpointRegistry> ? <K>(alias: K) => ServingEndpointHandle<ServingEndpointRegistry[K]["request"], ServingEndpointRegistry[K]["response"]> : {
5
+ <K> (alias: K): ServingEndpointHandle<ServingEndpointRegistry[K]["request"], ServingEndpointRegistry[K]["response"]>;
6
+ (): ServingEndpointHandle<never, never>;
7
+ };
5
8
 
6
9
  ```
7
10
 
8
11
  Factory function returned by `AppKit.serving`.
9
12
 
10
- This is a conditional type that adapts based on whether `ServingEndpointRegistry` has been populated via module augmentation (generated by `appKitServingTypesPlugin()`):
13
+ Adapts based on the `ServingEndpointRegistry` state:
11
14
 
12
- * **Registry empty (default):** `(alias?: string) => ServingEndpointHandle` — accepts any alias string with untyped request/response.
13
- * **Registry populated:** `<K>(alias: K) => ServingEndpointHandle<...>` restricts `alias` to known endpoint keys and infers typed request/response from the registry entry.
15
+ * **Empty (default):** `(alias?: string) => ServingEndpointHandle` — any string, untyped.
16
+ * **Single key:** alias optional — `serving()` returns the typed handle for the only endpoint.
17
+ * **Multiple keys:** alias required — must specify which endpoint.
14
18
 
15
- Run `appKitServingTypesPlugin()` in your Vite config to generate the registry augmentation and enable full type safety.
19
+ Run `npx appkit generate-types` or start the dev server to generate the registry.
@@ -0,0 +1,8 @@
1
+ # Variable: READ\_ACTIONS
2
+
3
+ ```ts
4
+ const READ_ACTIONS: ReadonlySet<FileAction>;
5
+
6
+ ```
7
+
8
+ Actions that only read data.
@@ -0,0 +1,8 @@
1
+ # Variable: WRITE\_ACTIONS
2
+
3
+ ```ts
4
+ const WRITE_ACTIONS: ReadonlySet<FileAction>;
5
+
6
+ ```
7
+
8
+ Actions that mutate data.
@@ -20,6 +20,7 @@ Core library for building Databricks applications with type-safe SQL queries, pl
20
20
  | [ExecutionError](./docs/api/appkit/Class.ExecutionError.md) | Error thrown when an operation execution fails. Use for statement failures, canceled operations, or unexpected states. |
21
21
  | [InitializationError](./docs/api/appkit/Class.InitializationError.md) | Error thrown when a service or component is not properly initialized. Use when accessing services before they are ready. |
22
22
  | [Plugin](./docs/api/appkit/Class.Plugin.md) | Base abstract class for creating AppKit plugins. |
23
+ | [PolicyDeniedError](./docs/api/appkit/Class.PolicyDeniedError.md) | Thrown when a policy denies an action. |
23
24
  | [ResourceRegistry](./docs/api/appkit/Class.ResourceRegistry.md) | Central registry for tracking plugin resource requirements. Deduplication uses type + resourceKey (machine-stable); alias is for display only. |
24
25
  | [ServerError](./docs/api/appkit/Class.ServerError.md) | Error thrown when server lifecycle operations fail. Use for server start/stop issues, configuration conflicts, etc. |
25
26
  | [TunnelError](./docs/api/appkit/Class.TunnelError.md) | Error thrown when remote tunnel operations fail. Use for tunnel connection issues, message parsing failures, etc. |
@@ -33,6 +34,8 @@ Core library for building Databricks applications with type-safe SQL queries, pl
33
34
  | [CacheConfig](./docs/api/appkit/Interface.CacheConfig.md) | Configuration for the CacheInterceptor. Controls TTL, size limits, storage backend, and probabilistic cleanup. |
34
35
  | [DatabaseCredential](./docs/api/appkit/Interface.DatabaseCredential.md) | Database credentials with OAuth token for Postgres connection |
35
36
  | [EndpointConfig](./docs/api/appkit/Interface.EndpointConfig.md) | - |
37
+ | [FilePolicyUser](./docs/api/appkit/Interface.FilePolicyUser.md) | Minimal user identity passed to the policy function. |
38
+ | [FileResource](./docs/api/appkit/Interface.FileResource.md) | Describes the file or directory being acted upon. |
36
39
  | [GenerateDatabaseCredentialRequest](./docs/api/appkit/Interface.GenerateDatabaseCredentialRequest.md) | Request parameters for generating database OAuth credentials |
37
40
  | [ITelemetry](./docs/api/appkit/Interface.ITelemetry.md) | Plugin-facing interface for OpenTelemetry instrumentation. Provides a thin abstraction over OpenTelemetry APIs for plugins. |
38
41
  | [LakebasePoolConfig](./docs/api/appkit/Interface.LakebasePoolConfig.md) | Configuration for creating a Lakebase connection pool |
@@ -50,21 +53,25 @@ Core library for building Databricks applications with type-safe SQL queries, pl
50
53
 
51
54
  ## Type Aliases[​](#type-aliases "Direct link to Type Aliases")
52
55
 
53
- | Type Alias | Description |
54
- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
55
- | [ConfigSchema](./docs/api/appkit/TypeAlias.ConfigSchema.md) | Configuration schema definition for plugin config. Re-exported from the standard JSON Schema Draft 7 types. |
56
- | [ExecutionResult](./docs/api/appkit/TypeAlias.ExecutionResult.md) | Discriminated union for plugin execution results. |
57
- | [IAppRouter](./docs/api/appkit/TypeAlias.IAppRouter.md) | Express router type for plugin route registration |
58
- | [PluginData](./docs/api/appkit/TypeAlias.PluginData.md) | Tuple of plugin class, config, and name. Created by `toPlugin()` and passed to `createApp()`. |
59
- | [ResourcePermission](./docs/api/appkit/TypeAlias.ResourcePermission.md) | Union of all possible permission levels across all resource types. |
60
- | [ServingFactory](./docs/api/appkit/TypeAlias.ServingFactory.md) | Factory function returned by `AppKit.serving`. |
61
- | [ToPlugin](./docs/api/appkit/TypeAlias.ToPlugin.md) | Factory function type returned by `toPlugin()`. Accepts optional config and returns a PluginData tuple. |
56
+ | Type Alias | Description |
57
+ | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
58
+ | [ConfigSchema](./docs/api/appkit/TypeAlias.ConfigSchema.md) | Configuration schema definition for plugin config. Re-exported from the standard JSON Schema Draft 7 types. |
59
+ | [ExecutionResult](./docs/api/appkit/TypeAlias.ExecutionResult.md) | Discriminated union for plugin execution results. |
60
+ | [FileAction](./docs/api/appkit/TypeAlias.FileAction.md) | Every action the files plugin can perform. |
61
+ | [FilePolicy](./docs/api/appkit/TypeAlias.FilePolicy.md) | A policy function that decides whether `user` may perform `action` on `resource`. Return `true` to allow, `false` to deny. |
62
+ | [IAppRouter](./docs/api/appkit/TypeAlias.IAppRouter.md) | Express router type for plugin route registration |
63
+ | [PluginData](./docs/api/appkit/TypeAlias.PluginData.md) | Tuple of plugin class, config, and name. Created by `toPlugin()` and passed to `createApp()`. |
64
+ | [ResourcePermission](./docs/api/appkit/TypeAlias.ResourcePermission.md) | Union of all possible permission levels across all resource types. |
65
+ | [ServingFactory](./docs/api/appkit/TypeAlias.ServingFactory.md) | Factory function returned by `AppKit.serving`. |
66
+ | [ToPlugin](./docs/api/appkit/TypeAlias.ToPlugin.md) | Factory function type returned by `toPlugin()`. Accepts optional config and returns a PluginData tuple. |
62
67
 
63
68
  ## Variables[​](#variables "Direct link to Variables")
64
69
 
65
- | Variable | Description |
66
- | ---------------------------------------------- | -------------------- |
67
- | [sql](./docs/api/appkit/Variable.sql.md) | SQL helper namespace |
70
+ | Variable | Description |
71
+ | ------------------------------------------------------------------- | ---------------------------- |
72
+ | [READ\_ACTIONS](./docs/api/appkit/Variable.READ_ACTIONS.md) | Actions that only read data. |
73
+ | [sql](./docs/api/appkit/Variable.sql.md) | SQL helper namespace |
74
+ | [WRITE\_ACTIONS](./docs/api/appkit/Variable.WRITE_ACTIONS.md) | Actions that mutate data. |
68
75
 
69
76
  ## Functions[​](#functions "Direct link to Functions")
70
77
 
@@ -4,7 +4,9 @@ AppKit can automatically generate TypeScript types for your SQL queries, providi
4
4
 
5
5
  ## Goal[​](#goal "Direct link to Goal")
6
6
 
7
- Generate `client/src/appKitTypes.d.ts` so query keys, parameters, and result rows are type-safe.
7
+ Generate type-safe TypeScript declarations for query keys, parameters, and result rows.
8
+
9
+ All generated files live in `shared/appkit-types/`, one per plugin (e.g. `analytics.d.ts`). They use [`declare module`](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) to augment existing interfaces, so the types apply globally — you never need to import them. TypeScript auto-discovers them through `"include": ["shared/appkit-types"]` in your tsconfig.
8
10
 
9
11
  ## Vite plugin: `appKitTypesPlugin`[​](#vite-plugin-appkittypesplugin "Direct link to vite-plugin-appkittypesplugin")
10
12
 
@@ -12,7 +14,7 @@ The recommended approach is to use the Vite plugin, which watches your SQL files
12
14
 
13
15
  ### Configuration[​](#configuration "Direct link to Configuration")
14
16
 
15
- * `outFile?: string` - Output file path (default: `src/appKitTypes.d.ts`)
17
+ * `outFile?: string` - Output file path (default: `shared/appkit-types/analytics.d.ts`)
16
18
  * `watchFolders?: string[]` - Folders to watch for SQL files (default: `["../config/queries"]`)
17
19
 
18
20
  ### Example[​](#example "Direct link to Example")
@@ -27,7 +29,6 @@ export default defineConfig({
27
29
  plugins: [
28
30
  react(),
29
31
  appKitTypesPlugin({
30
- outFile: "src/appKitTypes.d.ts",
31
32
  watchFolders: ["../config/queries"],
32
33
  }),
33
34
  ],
@@ -54,14 +55,14 @@ npx @databricks/appkit generate-types [rootDir] [outFile] [warehouseId]
54
55
  * Generate types using warehouse ID from environment
55
56
 
56
57
  ```bash
57
- npx @databricks/appkit generate-types . client/src/appKitTypes.d.ts
58
+ npx @databricks/appkit generate-types . shared/appkit-types/analytics.d.ts
58
59
 
59
60
  ```
60
61
 
61
62
  * Generate types using warehouse ID explicitly
62
63
 
63
64
  ```bash
64
- npx @databricks/appkit generate-types . client/src/appKitTypes.d.ts abc123...
65
+ npx @databricks/appkit generate-types . shared/appkit-types/analytics.d.ts abc123...
65
66
 
66
67
  ```
67
68
 
package/docs/faq.md CHANGED
@@ -6,14 +6,14 @@
6
6
 
7
7
  AppKit provides built-in integrations with the following Databricks services via its [plugin system](./docs/plugins.md):
8
8
 
9
- | Plugin | Databricks Service | What It Does |
10
- | ---------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------- |
11
- | [Analytics](./docs/plugins/analytics.md) | SQL Warehouses | Execute parameterized SQL queries with built-in caching, retry, and Arrow support |
12
- | [Lakebase](./docs/plugins/lakebase.md) | Lakebase Autoscaling (PostgreSQL) | Relational database access via standard pg.Pool with automatic OAuth token refresh |
13
- | [Genie](./docs/plugins/genie.md) | AI/BI Genie Spaces | Natural language data queries with conversation management and streaming |
14
- | [Files](./docs/plugins/files.md) | Unity Catalog Volumes | Multi-volume file operations (list, read, upload, download, delete, preview) |
15
- | [Serving](./docs/plugins/serving.md) | Model Serving | Authenticated proxy to Model Serving endpoints with invoke and streaming support |
16
- | [Server](./docs/plugins/server.md) | N/A | Express HTTP server with static file serving, Vite dev mode, and plugin route injection |
9
+ | Plugin | Databricks Service | What It Does |
10
+ | ------------------------------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------- |
11
+ | [Analytics](./docs/plugins/analytics.md) | SQL Warehouses | Execute parameterized SQL queries with built-in caching, retry, and Arrow support |
12
+ | [Lakebase](./docs/plugins/lakebase.md) | Lakebase Autoscaling (PostgreSQL) | Relational database access via standard pg.Pool with automatic OAuth token refresh |
13
+ | [Genie](./docs/plugins/genie.md) | AI/BI Genie Spaces | Natural language data queries with conversation management and streaming |
14
+ | [Files](./docs/plugins/files.md) | Unity Catalog Volumes | Multi-volume file operations (list, read, upload, download, delete, preview) |
15
+ | [Model Serving](./docs/plugins/model-serving.md) | Model Serving | Authenticated proxy to Model Serving endpoints with invoke and streaming support |
16
+ | [Server](./docs/plugins/server.md) | N/A | Express HTTP server with static file serving, Vite dev mode, and plugin route injection |
17
17
 
18
18
  Stay tuned for new plugins as we constantly expand integrations!
19
19
 
@@ -139,7 +139,7 @@ function SpendTable() {
139
139
  Augment the `QueryRegistry` interface to get full type inference on parameters and results:
140
140
 
141
141
  ```ts
142
- // config/appKitTypes.d.ts
142
+ // shared/appkit-types/analytics.d.ts
143
143
  declare module "@databricks/appkit-ui/react" {
144
144
  interface QueryRegistry {
145
145
  spend_summary: {
@@ -3,8 +3,12 @@
3
3
  If you need custom API routes or background logic, implement an AppKit plugin. The fastest way is to use the CLI:
4
4
 
5
5
  ```bash
6
+ # Interactive
6
7
  npx @databricks/appkit plugin create
7
8
 
9
+ # Non-interactive
10
+ npx @databricks/appkit plugin create --placement in-repo --path plugins/my-plugin --name my-plugin --description "My plugin" --force
11
+
8
12
  ```
9
13
 
10
14
  For a deeper understanding of the plugin structure, read on.
@@ -38,7 +38,6 @@ Exported from `@databricks/appkit`:
38
38
  * `getWorkspaceClient()`: Returns the appropriate WorkspaceClient for current context
39
39
  * `getWarehouseId()`: `Promise<string>` (from `DATABRICKS_WAREHOUSE_ID` or auto-selected in dev)
40
40
  * `getWorkspaceId()`: `Promise<string>` (from `DATABRICKS_WORKSPACE_ID` or fetched)
41
- * `isInUserContext()`: Returns `true` if currently executing in user context
42
41
 
43
42
  ## Development mode behavior[​](#development-mode-behavior "Direct link to Development mode behavior")
44
43
 
@@ -12,6 +12,7 @@ File operations against Databricks Unity Catalog Volumes. Supports listing, read
12
12
  * Automatic cache invalidation on write operations
13
13
  * Custom content type mappings
14
14
  * Per-user execution context (OBO)
15
+ * **Access policies**: Per-volume policy functions that gate read and write operations
15
16
 
16
17
  ## Basic usage[​](#basic-usage "Direct link to Basic usage")
17
18
 
@@ -75,6 +76,8 @@ interface IFilesConfig {
75
76
  }
76
77
 
77
78
  interface VolumeConfig {
79
+ /** Access policy for this volume. */
80
+ policy?: FilePolicy;
78
81
  /** Maximum upload size in bytes for this volume. Overrides plugin-level default. */
79
82
  maxUploadSize?: number;
80
83
  /** Map of file extensions to MIME types for this volume. Overrides plugin-level default. */
@@ -99,6 +102,121 @@ files({
99
102
 
100
103
  ```
101
104
 
105
+ ### Permission model[​](#permission-model "Direct link to Permission model")
106
+
107
+ There are three layers of access control in the files plugin. Understanding how they interact is critical for securing your app:
108
+
109
+ ```text
110
+ ┌─────────────────────────────────────────────────┐
111
+ │ Unity Catalog grants │
112
+ │ (WRITE_VOLUME on the SP — set at deploy time) │
113
+ ├─────────────────────────────────────────────────┤
114
+ │ Execution identity │
115
+ │ HTTP routes → always service principal │
116
+ │ Programmatic → SP by default, asUser() for OBO │
117
+ ├─────────────────────────────────────────────────┤
118
+ │ File policies │
119
+ │ Per-volume (action, resource, user) → boolean │
120
+ │ Only app-level gate for HTTP routes │
121
+ └─────────────────────────────────────────────────┘
122
+
123
+ ```
124
+
125
+ * **UC grants** control what the service principal can do at the Databricks level. These are set at deploy time via `app.yaml` resource bindings. The SP needs `WRITE_VOLUME` — the plugin declares this via resource requirements.
126
+ * **Execution identity** determines whose credentials are used for the actual API call. HTTP routes always use the SP. The programmatic API uses SP by default but supports `asUser(req)` for OBO.
127
+ * **File policies** are application-level checks evaluated **before** the API call. They receive the requesting user's identity (from the `x-forwarded-user` header) and decide allow/deny. This is the only gate that distinguishes between users on HTTP routes.
128
+
129
+ warning
130
+
131
+ Since HTTP routes always execute as the service principal, removing a user's UC `WRITE_VOLUME` grant has **no effect** on HTTP access — the SP's grant is what's used. Policies are how you restrict what individual users can do through your app.
132
+
133
+ New in v0.21.0
134
+
135
+ File policies are new. Volumes without an explicit policy now default to `publicRead()`, which **denies all write operations** (`upload`, `mkdir`, `delete`). If your app relies on write access, set an explicit policy — for example `files.policy.allowAll()` — on each volume that needs it.
136
+
137
+ #### Access policies[​](#access-policies "Direct link to Access policies")
138
+
139
+ Attach a policy to a volume to control which actions are allowed:
140
+
141
+ ```ts
142
+ import { files } from "@databricks/appkit";
143
+
144
+ files({
145
+ volumes: {
146
+ uploads: { policy: files.policy.publicRead() },
147
+ },
148
+ });
149
+
150
+ ```
151
+
152
+ #### Actions[​](#actions "Direct link to Actions")
153
+
154
+ Policies receive an action string. The full list, split by category:
155
+
156
+ | Category | Actions |
157
+ | -------- | ------------------------------------------------------------------ |
158
+ | Read | `list`, `read`, `download`, `raw`, `exists`, `metadata`, `preview` |
159
+ | Write | `upload`, `mkdir`, `delete` |
160
+
161
+ #### Built-in policies[​](#built-in-policies "Direct link to Built-in policies")
162
+
163
+ | Helper | Allows | Denies |
164
+ | --------------------------- | ---------------- | ----------------- |
165
+ | `files.policy.publicRead()` | all read actions | all write actions |
166
+ | `files.policy.allowAll()` | everything | nothing |
167
+ | `files.policy.denyAll()` | nothing | everything |
168
+
169
+ #### Composing policies[​](#composing-policies "Direct link to Composing policies")
170
+
171
+ Combine built-in and custom policies with three combinators:
172
+
173
+ * **`files.policy.all(a, b)`** — AND: all policies must allow. Short-circuits on first denial.
174
+ * **`files.policy.any(a, b)`** — OR: at least one policy must allow. Short-circuits on first allow.
175
+ * **`files.policy.not(p)`** — Inverts a policy. For example, `not(publicRead())` yields a write-only policy (useful for ingestion/drop-box volumes).
176
+
177
+ ```ts
178
+ // Read-only for regular users, full access for the service principal
179
+ files({
180
+ volumes: {
181
+ shared: {
182
+ policy: files.policy.any(
183
+ (_action, _resource, user) => !!user.isServicePrincipal,
184
+ files.policy.publicRead(),
185
+ ),
186
+ },
187
+ },
188
+ });
189
+
190
+ ```
191
+
192
+ #### Custom policies[​](#custom-policies "Direct link to Custom policies")
193
+
194
+ `FilePolicy` is a function `(action, resource, user) → boolean | Promise<boolean>`, so you can inline arbitrary logic:
195
+
196
+ ```ts
197
+ import { type FilePolicy, WRITE_ACTIONS } from "@databricks/appkit";
198
+
199
+ const ADMIN_IDS = ["admin-sp-id", "lead-user-id"];
200
+
201
+ const adminOnly: FilePolicy = (action, _resource, user) => {
202
+ if (WRITE_ACTIONS.has(action)) {
203
+ return ADMIN_IDS.includes(user.id);
204
+ }
205
+ return true; // reads allowed for everyone
206
+ };
207
+
208
+ files({
209
+ volumes: { reports: { policy: adminOnly } },
210
+ });
211
+
212
+ ```
213
+
214
+ #### Enforcement[​](#enforcement "Direct link to Enforcement")
215
+
216
+ * **HTTP routes**: Policy checked before every operation. Denied → `403` JSON response with `Policy denied "{action}" on volume "{volumeKey}"`.
217
+ * **Programmatic API**: Policy checked on both `appkit.files("vol").list()` (SP identity, `isServicePrincipal: true`) and `appkit.files("vol").asUser(req).list()` (user identity). Denied → throws `PolicyDeniedError`.
218
+ * **No policy configured**: Defaults to `files.policy.publicRead()` — read actions are allowed, write actions are denied. A startup warning is logged encouraging you to set an explicit policy.
219
+
102
220
  ### Custom content types[​](#custom-content-types "Direct link to Custom content types")
103
221
 
104
222
  Override or extend the built-in extension → MIME map:
@@ -118,7 +236,7 @@ Dangerous MIME types (`text/html`, `text/javascript`, `application/javascript`,
118
236
 
119
237
  ## HTTP routes[​](#http-routes "Direct link to HTTP routes")
120
238
 
121
- Routes are mounted at `/api/files/*`. All routes except `/volumes` execute in user context via `asUser(req)`.
239
+ Routes are mounted at `/api/files/*`. All routes execute as the service principal. Policy enforcement checks user identity (from the `x-forwarded-user` header) before allowing operations — see [Access policies](#access-policies).
122
240
 
123
241
  | Method | Path | Query / Body | Response |
124
242
  | ------ | ---------------------- | ---------------------------- | ------------------------------------------------------------ |
@@ -242,7 +360,36 @@ interface FilePreview extends FileMetadata {
242
360
  isImage: boolean;
243
361
  }
244
362
 
363
+ type FileAction =
364
+ | "list" | "read" | "download" | "raw"
365
+ | "exists" | "metadata" | "preview"
366
+ | "upload" | "mkdir" | "delete";
367
+
368
+ interface FileResource {
369
+ /** Relative path within the volume. */
370
+ path: string;
371
+ /** The volume key (e.g. `"uploads"`). */
372
+ volume: string;
373
+ /** Content length in bytes — only present for uploads. */
374
+ size?: number;
375
+ }
376
+
377
+ interface FilePolicyUser {
378
+ /** User ID from the `x-forwarded-user` header. */
379
+ id: string;
380
+ /** `true` when the caller is the service principal (direct SDK call, not `asUser`). */
381
+ isServicePrincipal?: boolean;
382
+ }
383
+
384
+ type FilePolicy = (
385
+ action: FileAction,
386
+ resource: FileResource,
387
+ user: FilePolicyUser,
388
+ ) => boolean | Promise<boolean>;
389
+
245
390
  interface VolumeConfig {
391
+ /** Access policy for this volume. */
392
+ policy?: FilePolicy;
246
393
  /** Maximum upload size in bytes for this volume. */
247
394
  maxUploadSize?: number;
248
395
  /** Map of file extensions to MIME types for this volume. */
@@ -280,7 +427,7 @@ Built-in extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.svg`, `.bmp`, `
280
427
 
281
428
  ## User context[​](#user-context "Direct link to User context")
282
429
 
283
- Routes use `this.asUser(req)` so operations execute with the requesting user's Databricks credentials (on-behalf-of / OBO). The `/volumes` route is the only exception since it only reads plugin config.
430
+ HTTP routes always execute as the **service principal** — the SP's Databricks credentials are used for all API calls. User identity is extracted from the `x-forwarded-user` header and passed to the volume's [access policy](#access-policies) for authorization. This means UC grants on the SP (not individual users) determine what operations are possible, while policies control what each user is allowed to do through the app.
284
431
 
285
432
  The programmatic API returns a `VolumeHandle` that exposes all `VolumeAPI` methods directly (service principal) and an `asUser(req)` method for OBO access. Calling any method without `asUser()` logs a warning encouraging OBO usage but does not throw. OBO access is strongly recommended for production use.
286
433
 
@@ -305,6 +452,7 @@ All errors return JSON:
305
452
  | Status | Description |
306
453
  | ------ | ------------------------------------------------------------- |
307
454
  | 400 | Missing or invalid `path` parameter |
455
+ | 403 | Policy denied "`{action}`" on volume "`{volumeKey}`" |
308
456
  | 404 | Unknown volume key |
309
457
  | 413 | Upload exceeds `maxUploadSize` |
310
458
  | 500 | Operation failed (SDK, network, upstream, or unhandled error) |
@@ -1,4 +1,4 @@
1
- # Serving plugin
1
+ # Model Serving plugin
2
2
 
3
3
  Provides an authenticated proxy to [Databricks Model Serving](https://docs.databricks.com/aws/en/machine-learning/model-serving) endpoints, with invoke and streaming support.
4
4
 
@@ -6,19 +6,30 @@ AppKit includes a CLI for managing plugins. All commands are available under `np
6
6
 
7
7
  ## Create a plugin[​](#create-a-plugin "Direct link to Create a plugin")
8
8
 
9
- Scaffold a new plugin interactively:
9
+ Scaffold a new plugin interactively or via flags:
10
10
 
11
11
  ```bash
12
+ # Interactive mode (prompts for all options)
12
13
  npx @databricks/appkit plugin create
13
14
 
15
+ # Non-interactive mode (all required flags provided)
16
+ npx @databricks/appkit plugin create \
17
+ --placement in-repo \
18
+ --path plugins/my-plugin \
19
+ --name my-plugin \
20
+ --description "My custom plugin" \
21
+ --resources sql_warehouse \
22
+ --force
23
+
14
24
  ```
15
25
 
16
- The wizard walks you through:
26
+ In interactive mode, the wizard walks you through:
17
27
 
18
28
  * **Placement**: In your repository (e.g. `plugins/my-plugin`) or as a standalone package
19
29
  * **Metadata**: Name, display name, description
20
30
  * **Resources**: Which Databricks resources the plugin needs (SQL Warehouse, Secret, etc.) and whether each is required or optional
21
- * **Optional fields**: Author, version, license
31
+
32
+ In non-interactive mode, `--placement`, `--path`, `--name`, and `--description` are required. Resources can be specified as a comma-separated list (`--resources sql_warehouse,volume`) or as JSON for full control (`--resources-json '[{"type":"sql_warehouse","permission":"CAN_MANAGE"}]'`). For all available options, run `npx @databricks/appkit plugin create --help`.
22
33
 
23
34
  The command generates a complete plugin scaffold with `manifest.json` and a TypeScript plugin class that imports the manifest directly — ready to register in your app.
24
35
 
@@ -91,12 +102,17 @@ npx @databricks/appkit plugin list --json
91
102
 
92
103
  ## Add a resource to a plugin[​](#add-a-resource-to-a-plugin "Direct link to Add a resource to a plugin")
93
104
 
94
- Interactively add a new resource requirement to an existing plugin manifest. **Requires `manifest.json`** in the plugin directory (the command edits it in place; it does not modify `manifest.js`):
105
+ Add a new resource requirement to an existing plugin manifest. **Requires `manifest.json`** in the plugin directory (the command edits it in place; it does not modify `manifest.js`):
95
106
 
96
107
  ```bash
108
+ # Interactive mode
97
109
  npx @databricks/appkit plugin add-resource
98
-
99
- # Or specify the plugin directory
100
110
  npx @databricks/appkit plugin add-resource --path plugins/my-plugin
101
111
 
112
+ # Non-interactive mode (--type triggers flag-based mode)
113
+ npx @databricks/appkit plugin add-resource --path plugins/my-plugin --type sql_warehouse
114
+ npx @databricks/appkit plugin add-resource --path plugins/my-plugin --type volume --no-required --dry-run
115
+
102
116
  ```
117
+
118
+ In non-interactive mode, only `--type` is required — all other fields (permission, resource key, field env vars) default to sensible values from the schema. Use `--dry-run` to preview the updated manifest without writing. For all available options, run `npx @databricks/appkit plugin add-resource --help`.