Every grant pulsobot-installer makes, per tier (ADR
0046, #759). The tables are generated from the permission plan in
cmd/installer/internal/deploy/permissions.go; a test fails
when they drift. The security pack (#764) builds on this page.
To change a grant, change the plan, then regenerate:
go test ./cmd/installer/internal/deploy -run PermissionsDoc -update-permissions-docproduct): its Delta schema, its volumes, its
secret scope, its Lakebase database. Nothing it holds on a customer
object is write-level. Tests pin both facts
(TestPermissionPlan_DiagnoseIsReadOnlyOnTheCustomerEstate,
TestPermissionPlan_DiagnoseWritesAreExactlyTheseOnes).CAN_QUERY on the Foundation Model
endpoints, granted by --prepare-optimize or
upgrade (second table), never by a Diagnose install.--prepare-optimize (or
pulsobot-installer upgrade): the second table. The agent
jobs, their two service principals (created with account-level OAuth
during that run only), and the grants they need. The agents stay dormant
until a valid Optimize Licence is present.FF_REMEDIATION=false skips the second table even with
--prepare-optimize.
The installing identity, which the jobs run as, holds
WRITE on the product secret scope. The rules job needs it
to install a licence it picked up from the delivery store (#756), so the
API and the jobs see the new licence at their next refresh, with no
human step. WRITE cannot change the scope's ACLs or delete
the scope; MANAGE can, and the product does not hold
it.
The product schema grants (USE_CATALOG …
CREATE_FUNCTION on
<catalog>.<schema>) also go to
account users, so human users can read and
write the product's tables. They cover only the product's own
schema, never a customer object.
They are not narrowed to read-only because a product flow needs them:
the API's service principal writes its Delta tables (Accounts, account
and cloud settings, instance discounts) through
account users — it has no schema grant of its own
(GrantAppVolumeAccess gives it SELECT on five
tables only). Narrowing account users to
USE_CATALOG/USE_SCHEMA/SELECT
first needs explicit write grants to the app's service principal
(follow-up).
The account-admin credentials used by --prepare-optimize
or upgrade
(DATABRICKS_ACCOUNT_CLIENT_ID/_SECRET, or a
profile) are passed only to the account CLI calls of that run. They are
never written to the secret scope, the install state file
(~/.pulsobot/…, config only) or a bundle variable
(TestAccountAdminCredentialsAreNeverPersisted,
TestSaveConfigToState_NeverStoresCredentials). The scope
holds the agents' own service-principal credentials, which the installer
creates.
| Principal | Object | Privilege | Owner | Applied by | Why |
|---|---|---|---|---|---|
| installing identity (the jobs' run_as) | catalog system |
USE_CATALOG |
customer | GrantPermissions |
extraction reads system tables |
| installing identity (the jobs' run_as) | schema system.compute |
USE_SCHEMA |
customer | GrantPermissions |
extraction reads system tables |
| installing identity (the jobs' run_as) | schema system.billing |
USE_SCHEMA |
customer | GrantPermissions |
extraction reads system tables |
| installing identity (the jobs' run_as) | schema system.lakeflow |
USE_SCHEMA |
customer | GrantPermissions |
extraction reads system tables |
| installing identity (the jobs' run_as) + account users | catalog <catalog> |
USE_CATALOG |
product | GrantPermissions |
the jobs and the API read and write the product's own Delta tables; the API's service principal has no grant of its own for its Delta writes (accounts, settings) and relies on this one, so humans can read and write the product's schema too |
| installing identity (the jobs' run_as) + account users | schema <catalog>.<schema> |
USE_SCHEMA |
product | GrantPermissions |
the jobs and the API read and write the product's own Delta tables; the API's service principal has no grant of its own for its Delta writes (accounts, settings) and relies on this one, so humans can read and write the product's schema too |
| installing identity (the jobs' run_as) + account users | schema <catalog>.<schema> |
SELECT |
product | GrantPermissions |
the jobs and the API read and write the product's own Delta tables; the API's service principal has no grant of its own for its Delta writes (accounts, settings) and relies on this one, so humans can read and write the product's schema too |
| installing identity (the jobs' run_as) + account users | schema <catalog>.<schema> |
MODIFY |
product | GrantPermissions |
the jobs and the API read and write the product's own Delta tables; the API's service principal has no grant of its own for its Delta writes (accounts, settings) and relies on this one, so humans can read and write the product's schema too |
| installing identity (the jobs' run_as) + account users | schema <catalog>.<schema> |
CREATE_TABLE |
product | GrantPermissions |
the jobs and the API read and write the product's own Delta tables; the API's service principal has no grant of its own for its Delta writes (accounts, settings) and relies on this one, so humans can read and write the product's schema too |
| installing identity (the jobs' run_as) + account users | schema <catalog>.<schema> |
CREATE_FUNCTION |
product | GrantPermissions |
the jobs and the API read and write the product's own Delta tables; the API's service principal has no grant of its own for its Delta writes (accounts, settings) and relies on this one, so humans can read and write the product's schema too |
| installing identity (the jobs' run_as) | secret scope <scope> |
WRITE |
product | install.go Step 6a (ambientScopePermission) |
the one deliberate write outside the Delta schema: the rules job installs a picked-up licence (#756); WRITE cannot change ACLs or delete the scope |
| API app service principal | secret scope <scope> |
READ |
product | install.go Step 9b |
the API reads its licence and settings |
| installing identity (the jobs' run_as) | volumes <catalog>.<schema>.* |
READ_VOLUME |
product | GrantVolumeReadWriteToCurrentPrincipal |
upload and run the product binaries |
| installing identity (the jobs' run_as) | volumes <catalog>.<schema>.* |
WRITE_VOLUME |
product | GrantVolumeReadWriteToCurrentPrincipal |
upload binaries, cache the Rule Set Bundle |
| API app service principal | catalog <catalog> |
USE_CATALOG |
product | GrantAppVolumeAccess |
the API reads the product's own Delta tables |
| API app service principal | schema <catalog>.<schema> |
USE_SCHEMA |
product | GrantAppVolumeAccess |
the API reads the product's own Delta tables |
| API app service principal | tables <catalog>.<schema>.{silver_llm_analysis, raw_workload_code, workload_code_version, code_extraction_runs, gold_workspaces} |
SELECT |
product | GrantAppVolumeAccess |
table-level, not schema-wide: raw ingestion tables carry unredacted secrets |
| API app service principal | volumes binaries, staging, remediation_artifacts |
READ_VOLUME |
product | GrantAppVolumeAccess |
the app downloads its binary and reads the Rule Set Bundle |
| API app service principal | SQL warehouse <warehouse> |
CAN_USE |
customer | api_app.yml resources |
the API queries Delta |
| workspace group users | app <product>-api |
CAN_USE |
product | api_app.yml permissions |
workspace users open the dashboard |
| workspace group users | product jobs |
CAN_VIEW |
product | resources/*_job.yml permissions |
users see the product's own job runs |
| API app service principal | product jobs |
CAN_VIEW |
product | GrantAppJobViewAccess |
/health reads the product's own job runs |
| account users + installing identity (the jobs' run_as) | catalog <product>_lakebase |
USE_CATALOG |
product | GrantGenieRemediationAccess |
Genie space reads the product's Lakebase catalog |
| account users + installing identity (the jobs' run_as) | schema <product>_lakebase.remediation |
USE_SCHEMA |
product | GrantGenieRemediationAccess |
Genie space reads the product's Lakebase catalog |
| account users + installing identity (the jobs' run_as) | schema <product>_lakebase.remediation |
SELECT |
product | GrantGenieRemediationAccess |
Genie space reads the product's Lakebase catalog |
| PULSOBOT_LAKEBASE_*_USERS (operator-chosen) | Lakebase database (public, remediation) |
SELECT / CRUD per list |
product | GrantLakebaseUsers |
only when the operator names users |
--prepare-optimize / upgrade| Principal | Object | Privilege | Owner | Applied by | Why |
|---|---|---|---|---|---|
| installing identity (the jobs' run_as) | Foundation Model serving endpoints in use |
CAN_QUERY |
customer | GrantFMEndpointPermissions (install.go Step 11) |
LLM code analysis (Optimize Tier, ADR 0046) |
| installing identity (the jobs' run_as) | Lakebase schema remediation |
SELECT, INSERT, UPDATE, DELETE |
product | GrantAmbientIdentityLakebaseAccess |
agent jobs write Remediation Orders |
| installing identity (the jobs' run_as) | remediated catalogs (PULSOBOT_REMEDIATED_CATALOGS) |
USE_CATALOG, USE_SCHEMA, SELECT |
customer | GrantVerificationAgentPermissions |
Verification Agent compares outputs |
| Test Agent service principal | account service principal (created) |
- |
customer | EnsureTestAgentIdentity (account-level OAuth) |
Test Agent's own identity |
| Test Agent service principal | remediated catalogs (read) + its sandbox schema |
USE_CATALOG, USE_SCHEMA, SELECT; CREATE on sandbox |
customer | GrantTestAgentPermissions |
Test Agent replays workloads in a sandbox |
| Deployment Agent service principal | account service principal (created) |
- |
customer | EnsureDeploymentAgentIdentity (account-level OAuth) |
Deployment Agent's own identity |
| Deployment Agent service principal | workspace |
ADMIN |
customer | GrantDeploymentAgentWorkspaceAdmin (account-level OAuth) |
ADR-0019: deploys remediated job definitions |
| Deployment Agent service principal | every job |
CAN_MANAGE |
customer | GrantDeploymentAgentJobsAccess |
ADR-0019: edits the jobs it remediates |
| agent jobs' run_as identities | secret scope <scope> |
READ |
product | install.go Step 9a |
agents read their own OAuth credential |