bt-cli 0.4.73__tar.gz → 0.6.0__tar.gz
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.
- {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/entitle/SKILL.md +166 -3
- bt_cli-0.6.0/.claude/skills/pf/SKILL.md +281 -0
- {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/pra/SKILL.md +3 -0
- bt_cli-0.6.0/.claude/skills/secrets/SKILL.md +19 -0
- bt_cli-0.6.0/.claude/skills/wlc/SKILL.md +452 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/CLAUDE.md +20 -14
- {bt_cli-0.4.73 → bt_cli-0.6.0}/PKG-INFO +59 -18
- {bt_cli-0.4.73 → bt_cli-0.6.0}/README.md +58 -17
- {bt_cli-0.4.73 → bt_cli-0.6.0}/bt-cli-cheatsheet.html +44 -0
- bt_cli-0.6.0/entitle-gaps-implementation-plan.md +378 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/pyproject.toml +1 -1
- bt_cli-0.6.0/scripts/sync-package-data.sh +77 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/__init__.py +1 -1
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/cli.py +50 -26
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/configure.py +15 -15
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/learn.py +2 -2
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/quick.py +5 -6
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/config.py +68 -30
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/config_file.py +36 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/output.py +57 -4
- bt_cli-0.6.0/src/bt_cli/data/CLAUDE.md +146 -0
- bt_cli-0.6.0/src/bt_cli/data/skills/entitle/SKILL.md +468 -0
- {bt_cli-0.4.73/.claude → bt_cli-0.6.0/src/bt_cli/data}/skills/epml/SKILL.md +24 -3
- bt_cli-0.6.0/src/bt_cli/data/skills/pf/SKILL.md +281 -0
- bt_cli-0.6.0/src/bt_cli/data/skills/pra/SKILL.md +219 -0
- bt_cli-0.6.0/src/bt_cli/data/skills/pws/SKILL.md +353 -0
- bt_cli-0.6.0/src/bt_cli/data/skills/secrets/SKILL.md +19 -0
- bt_cli-0.6.0/src/bt_cli/data/skills/session-analysis/SKILL.md +72 -0
- bt_cli-0.6.0/src/bt_cli/data/skills/wlc/SKILL.md +452 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/client/base.py +358 -19
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/__init__.py +17 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/_helpers.py +68 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/agent_tokens.py +402 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/auth.py +4 -2
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/bundles.py +65 -4
- bt_cli-0.6.0/src/bt_cli/entitle/commands/chats.py +162 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/directory_groups.py +84 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/forwards.py +392 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/integrations.py +421 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/permissions.py +139 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/policies.py +336 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/requests.py +22 -10
- bt_cli-0.6.0/src/bt_cli/entitle/commands/resources.py +279 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/roles.py +368 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/tasks.py +281 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/user_accounts.py +209 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/users.py +81 -5
- bt_cli-0.6.0/src/bt_cli/entitle/commands/webhooks.py +516 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/workflow_webhook_writes.py +304 -0
- bt_cli-0.6.0/src/bt_cli/entitle/commands/workflows.py +712 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/client_pkg.py +5 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_roles.py +4 -8
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_tmdategrps.py +5 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_usergrps.py +5 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/settings.py +5 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/groups.py +5 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/policies.py +16 -9
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/quick.py +16 -11
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/users.py +5 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/client/base.py +126 -16
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/__init__.py +6 -1
- bt_cli-0.6.0/src/bt_cli/pf/commands/_common.py +77 -0
- bt_cli-0.6.0/src/bt_cli/pf/commands/policies.py +568 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/users.py +25 -62
- bt_cli-0.6.0/src/bt_cli/pf/commands/workload_identities.py +259 -0
- bt_cli-0.6.0/src/bt_cli/pf/validation.py +104 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/group_policies.py +17 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/import_export.py +3 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jump_items.py +20 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/quick.py +9 -9
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/vault.py +10 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/accounts.py +14 -5
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/assets.py +13 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/clouds.py +8 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/config.py +5 -6
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/credentials.py +3 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/databases.py +18 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/directories.py +8 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/functional.py +17 -3
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/import_export.py +3 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/platforms.py +3 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/quick.py +9 -10
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/search.py +7 -8
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/secrets.py +51 -7
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/sessions.py +7 -6
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/systems.py +8 -4
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/users.py +9 -9
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/workgroups.py +7 -3
- bt_cli-0.6.0/src/bt_cli/wlc/__init__.py +1 -0
- bt_cli-0.6.0/src/bt_cli/wlc/client/__init__.py +5 -0
- {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/client/base.py +54 -21
- bt_cli-0.6.0/src/bt_cli/wlc/commands/__init__.py +37 -0
- {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/_hints.py +1 -1
- bt_cli-0.6.0/src/bt_cli/wlc/commands/access.py +216 -0
- bt_cli-0.6.0/src/bt_cli/wlc/commands/auth.py +84 -0
- {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/dynamic.py +17 -17
- {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/folders.py +7 -7
- {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/integrations.py +6 -6
- {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/leases.py +5 -5
- {bt_cli-0.4.73/src/bt_cli/secrets → bt_cli-0.6.0/src/bt_cli/wlc}/commands/static.py +8 -8
- bt_cli-0.6.0/src/bt_cli/wlc/commands/tree.py +102 -0
- bt_cli-0.6.0/src/bt_cli/wlc/models/__init__.py +1 -0
- bt_cli-0.6.0/src/bt_cli/wlc/policy.py +654 -0
- bt_cli-0.6.0/src/bt_cli/wlc/snapshot.py +40 -0
- bt_cli-0.6.0/tests/_runner.py +21 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/conftest.py +44 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_command_tree.py +22 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_json_stdout_purity.py +4 -10
- bt_cli-0.6.0/tests/core/test_package_data_sync.py +353 -0
- bt_cli-0.6.0/tests/data/entitle-api-reference.openapi.json +8930 -0
- bt_cli-0.6.0/tests/entitle/test_agent_tokens.py +322 -0
- bt_cli-0.6.0/tests/entitle/test_agent_tokens_writes.py +433 -0
- bt_cli-0.6.0/tests/entitle/test_chats.py +189 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle/test_client.py +59 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle/test_commands.py +326 -3
- bt_cli-0.6.0/tests/entitle/test_coverage.py +123 -0
- bt_cli-0.6.0/tests/entitle/test_directory_groups.py +222 -0
- bt_cli-0.6.0/tests/entitle/test_forwards.py +193 -0
- bt_cli-0.6.0/tests/entitle/test_forwards_writes.py +521 -0
- bt_cli-0.6.0/tests/entitle/test_integrations_writes.py +466 -0
- bt_cli-0.6.0/tests/entitle/test_policies_writes.py +413 -0
- bt_cli-0.6.0/tests/entitle/test_resources_writes.py +375 -0
- bt_cli-0.6.0/tests/entitle/test_roles_writes.py +390 -0
- bt_cli-0.6.0/tests/entitle/test_tasks.py +219 -0
- bt_cli-0.6.0/tests/entitle/test_tasks_writes.py +337 -0
- bt_cli-0.6.0/tests/entitle/test_user_accounts.py +260 -0
- bt_cli-0.6.0/tests/entitle/test_user_accounts_writes.py +419 -0
- bt_cli-0.6.0/tests/entitle/test_webhooks.py +279 -0
- bt_cli-0.6.0/tests/entitle/test_webhooks_writes.py +590 -0
- bt_cli-0.6.0/tests/entitle/test_workflow_webhook_writes.py +522 -0
- bt_cli-0.6.0/tests/entitle/test_workflows_writes.py +508 -0
- bt_cli-0.6.0/tests/pf/conftest.py +21 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pf/test_client.py +61 -2
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pf/test_commands.py +40 -0
- bt_cli-0.6.0/tests/pf/test_policies.py +485 -0
- bt_cli-0.6.0/tests/pf/test_workload_identities.py +244 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/test_jump_item_fixes.py +4 -10
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/test_commands.py +4 -10
- bt_cli-0.6.0/tests/wlc/conftest.py +21 -0
- {bt_cli-0.4.73/tests/secrets → bt_cli-0.6.0/tests/wlc}/test_client.py +9 -7
- {bt_cli-0.4.73/tests/secrets → bt_cli-0.6.0/tests/wlc}/test_commands.py +27 -14
- bt_cli-0.6.0/tests/wlc/test_policy.py +366 -0
- bt_cli-0.6.0/tests/wlc/test_rename.py +170 -0
- bt_cli-0.6.0/tests/wlc/test_tree_access.py +244 -0
- bt_cli-0.4.73/.claude/skills/entitle/SKILL.md +0 -231
- bt_cli-0.4.73/.claude/skills/pra/SKILL.md +0 -153
- bt_cli-0.4.73/.claude/skills/pws/SKILL.md +0 -198
- bt_cli-0.4.73/scripts/sync-package-data.sh +0 -21
- bt_cli-0.4.73/src/bt_cli/data/CLAUDE.md +0 -118
- bt_cli-0.4.73/src/bt_cli/data/skills/pf/SKILL.md +0 -116
- bt_cli-0.4.73/src/bt_cli/data/skills/secrets/SKILL.md +0 -229
- bt_cli-0.4.73/src/bt_cli/entitle/commands/integrations.py +0 -60
- bt_cli-0.4.73/src/bt_cli/entitle/commands/permissions.py +0 -70
- bt_cli-0.4.73/src/bt_cli/entitle/commands/policies.py +0 -97
- bt_cli-0.4.73/src/bt_cli/entitle/commands/resources.py +0 -131
- bt_cli-0.4.73/src/bt_cli/entitle/commands/roles.py +0 -74
- bt_cli-0.4.73/src/bt_cli/entitle/commands/workflows.py +0 -187
- bt_cli-0.4.73/src/bt_cli/secrets/__init__.py +0 -1
- bt_cli-0.4.73/src/bt_cli/secrets/client/__init__.py +0 -5
- bt_cli-0.4.73/src/bt_cli/secrets/commands/__init__.py +0 -23
- bt_cli-0.4.73/src/bt_cli/secrets/commands/auth.py +0 -67
- bt_cli-0.4.73/src/bt_cli/secrets/models/__init__.py +0 -1
- {bt_cli-0.4.73 → bt_cli-0.6.0}/.claude/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/epml/SKILL.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/.claude/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/pws/SKILL.md +0 -0
- {bt_cli-0.4.73/src/bt_cli/data → bt_cli-0.6.0/.claude}/skills/session-analysis/SKILL.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/.env.example +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/.github/workflows/ci.yml +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/.github/workflows/release.yml +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/.gitignore +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/assets/cli-help.png +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/assets/cli-output.png +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/bt-cli.spec +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/bt_entry.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/epml-clients-server-side-filters-plan.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/epml-implementation-plan.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/pf-implementation-plan.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/scripts/bt_entry.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/scripts/pf_onboard.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/commands/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/auth.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/client.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/command_tree.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/csv_utils.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/errors.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/prompts.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/core/rest_debug.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/data/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/data/skills/bt/SKILL.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/data/skills/epmw/SKILL.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/client/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/accounts.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/applications.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/audit.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/commands/jit.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/bundle.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/common.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/integration.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/permission.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/policy.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/resource.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/role.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/user.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/entitle/models/workflow.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/client/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/client/base.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/audit.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/auth.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/clients.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/external_apis.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/hosts.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/iolog.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/license.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/quick.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_cmdgrps.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_entitlement.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_hostgrps.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_policy.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_tests.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/rbp_tx.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/siems.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/commands/users.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epml/models/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/client/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/client/base.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/audits.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/auth.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/computers.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/events.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/requests.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/roles.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/commands/tasks.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/epmw/models/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/client/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/auth.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/machines.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/tokens.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pf/commands/user.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/client/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/client/base.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/auth.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jump_clients.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jump_groups.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/jumpoints.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/policies.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/teams.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/commands/users.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/common.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/group_policy.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jump_client.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jump_group.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jump_item.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/jumpoint.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/team.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/user.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pra/models/vault.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/base.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/beyondinsight.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/client/passwordsafe.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/attributes.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/audit.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/commands/auth.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/config.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/account.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/asset.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/common.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/src/bt_cli/pws/models/system.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_auth.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_config.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_config_file.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_errors.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_output.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/core/test_rest_debug.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/entitle-smoke-test.sh +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epml/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epml/test_client.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epml/test_commands.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw/test_client.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw/test_commands.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/epmw-quick-test-plan.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/fixtures/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/fixtures/responses.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/conftest.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/helpers.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_entitle_integration.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_epmw_integration.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_epmw_lifecycle.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pra_integration.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pra_lifecycle.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pws_integration.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/integration/test_pws_lifecycle.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pf/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/test_client.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra/test_commands.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra-smoke-test.sh +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pra-test-plan.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/__init__.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/test_client.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws/test_quick_sessions.py +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws-quick-test-plan.md +0 -0
- {bt_cli-0.4.73 → bt_cli-0.6.0}/tests/pws-smoke-test.sh +0 -0
- {bt_cli-0.4.73/tests/secrets → bt_cli-0.6.0/tests/wlc}/__init__.py +0 -0
|
@@ -5,6 +5,33 @@ description: Entitle commands for JIT access, bundles, workflows, and permission
|
|
|
5
5
|
|
|
6
6
|
# Entitle Commands (`bt entitle`)
|
|
7
7
|
|
|
8
|
+
## Two API tokens — read vs. write-on-behalf-of-user
|
|
9
|
+
|
|
10
|
+
Entitle issues two distinct kinds of bearer token. The CLI uses both:
|
|
11
|
+
|
|
12
|
+
| Token | Config field | Env var | Used for |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
| **Org / admin** | `entitle.api_key` | `BT_ENTITLE_API_KEY` | All read-only commands (`list`, `get`) and admin writes (revoke, delete) |
|
|
15
|
+
| **User-context** | `entitle.user_api_key` | `BT_ENTITLE_USER_API_KEY` | On-behalf-of-a-user writes — currently `bt entitle requests create` |
|
|
16
|
+
|
|
17
|
+
The org/admin token **cannot** create access requests because requests are
|
|
18
|
+
made *as* a person, not on behalf of the org. If only the admin token is
|
|
19
|
+
configured, `bt entitle requests create` exits early with a panel explaining
|
|
20
|
+
how to add a user token.
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
# ~/.bt-cli/config.yaml
|
|
24
|
+
profiles:
|
|
25
|
+
default:
|
|
26
|
+
entitle:
|
|
27
|
+
api_url: https://api.us.entitle.io
|
|
28
|
+
api_key: <ORG_TOKEN> # broad read access
|
|
29
|
+
user_api_key: <USER_TOKEN> # personal token, for `requests create`
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The user token is generated from `app.entitle.io` → user profile → API tokens
|
|
33
|
+
→ **Create user token** (different page from the org token).
|
|
34
|
+
|
|
8
35
|
## IMPORTANT: Destructive Operations
|
|
9
36
|
|
|
10
37
|
**ALWAYS confirm with the user before:**
|
|
@@ -296,10 +323,146 @@ bt entitle roles list --resource <resource_id>
|
|
|
296
323
|
**Kubernetes:** EKS, AKS, GKE, Rancher
|
|
297
324
|
**BeyondTrust:** Password Safe, PRA, Remote Support
|
|
298
325
|
|
|
326
|
+
## Newer command families
|
|
327
|
+
|
|
328
|
+
Added in 0.5.0; the CLI now covers 75 of the API's 77 operations.
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# Grant/revoke work items behind an access request (read + status only)
|
|
332
|
+
bt entitle tasks list
|
|
333
|
+
bt entitle tasks get <task-id>
|
|
334
|
+
bt entitle tasks set-status <task-id> --status todo|inProgress|done
|
|
335
|
+
bt entitle tasks archive <task-id>
|
|
336
|
+
|
|
337
|
+
# Agent tokens — these are real credentials
|
|
338
|
+
bt entitle agent-tokens list
|
|
339
|
+
bt entitle agent-tokens create --name "collector" -o json # token shown ONCE
|
|
340
|
+
bt entitle agent-tokens update <id> --name "new name"
|
|
341
|
+
bt entitle agent-tokens delete <id>
|
|
342
|
+
|
|
343
|
+
# Outbound webhooks — two separate families
|
|
344
|
+
bt entitle audit-webhooks list | get | create | update | enable | delete
|
|
345
|
+
bt entitle workflow-webhooks list | get | create | update | delete
|
|
346
|
+
|
|
347
|
+
# Delegation: send someone's queue to someone else
|
|
348
|
+
bt entitle request-forwards list | get | for-user <user-id> | create | delete
|
|
349
|
+
bt entitle review-forwards list | get | create | delete
|
|
350
|
+
|
|
351
|
+
# Identity plumbing
|
|
352
|
+
bt entitle directory-groups list --search "DYN-ROLE"
|
|
353
|
+
bt entitle user-accounts list | create | delete
|
|
354
|
+
bt entitle users pats <user-id>
|
|
355
|
+
bt entitle permissions account <account-id>
|
|
356
|
+
bt entitle chats channels --integration-type slack
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Writes were added to the families that previously had reads only: `roles`,
|
|
360
|
+
`policies`, `workflows`, `integrations` all gained create/update/delete, and
|
|
361
|
+
`resources` and `bundles` gained `update`.
|
|
362
|
+
|
|
363
|
+
## Update semantics: PUT merges, but lists inside it do not
|
|
364
|
+
|
|
365
|
+
Live-verified per family. **Every family merges** — a field you leave out of the
|
|
366
|
+
body keeps its current value, so no command does read-modify-write and a `curl`
|
|
367
|
+
sending one field is safe.
|
|
368
|
+
|
|
369
|
+
Two qualifications that matter more than the rule:
|
|
370
|
+
|
|
371
|
+
- **A list you *do* send replaces that list wholesale.** `--role`, `--tag`,
|
|
372
|
+
`--group`, `--duration` set the whole collection; they never append. Passing
|
|
373
|
+
`-g` alone drops every other target.
|
|
374
|
+
- **`workflows` merges at the top level but REPLACES its `rules` array.** A
|
|
375
|
+
two-rule workflow plus a PUT carrying one rule leaves **one rule**, and each
|
|
376
|
+
rule object must be complete (`400 "rules.0.inGroups must be an array"`).
|
|
377
|
+
|
|
378
|
+
## Writes that return 200 and do nothing
|
|
379
|
+
|
|
380
|
+
The most dangerous behaviour in this API. All live-verified — **verify a write
|
|
381
|
+
with a follow-up GET**, because the response will not tell you:
|
|
382
|
+
|
|
383
|
+
| Operation | Silently ignored |
|
|
384
|
+
|---|---|
|
|
385
|
+
| `POST /roles` | drops `prerequisitePermissions` (CLI issues a follow-up PUT) |
|
|
386
|
+
| `PUT /roles/{id}` | ignores `name`, `resource`, `virtualizedRole` |
|
|
387
|
+
| `PUT /integrations/{id}` | ignores `allowChangingAccountPermissions`, `allowCreatingAccounts`, `readonly` |
|
|
388
|
+
| `PUT /policies/{id}` | a `sortOrder` past the last policy keeps the old value |
|
|
389
|
+
| workflow webhooks | `approvalAlgorithmIds` is read-only |
|
|
390
|
+
| both webhook families | `{"enabled": false}` on create *and* update |
|
|
391
|
+
| integrations update | an unknown `--owner` (but create 404s on it) |
|
|
392
|
+
|
|
393
|
+
**`POST` on both forwards families is an UPSERT keyed on the forwarder** — a
|
|
394
|
+
second create for the same person returns the *same id* with the target
|
|
395
|
+
replaced, 200, no 409. "Create a forward for Alice" therefore silently
|
|
396
|
+
redirects her queue away from whoever it pointed at. Check with
|
|
397
|
+
`request-forwards for-user <id>` first.
|
|
398
|
+
|
|
399
|
+
**`POST /usersAccounts` is idempotent** on `(user, account)`: it returns the
|
|
400
|
+
pre-existing row with its original `createdAt` rather than creating a duplicate.
|
|
401
|
+
A script that deletes "the row it just created" will be deleting a real one.
|
|
402
|
+
|
|
403
|
+
## Pagination: the envelope tells you
|
|
404
|
+
|
|
405
|
+
`pagination` present in the response means the endpoint really pages. Absent
|
|
406
|
+
means `page`/`perPage` are **silently ignored** and one GET returns everything —
|
|
407
|
+
requesting page 2 hands back the same first row.
|
|
408
|
+
|
|
409
|
+
| Ignores paging | Really pages |
|
|
410
|
+
|---|---|
|
|
411
|
+
| `/tasks`, `/applications`, `/auditLogsWebhooks`, `/workflowsWebhooks` | `/agentTokens`, `/directoryGroups`, `/usersAccounts`, both `*Forwards` |
|
|
412
|
+
|
|
413
|
+
`perPage` caps at **100** and 400s above it. `--limit` means *maximum results*
|
|
414
|
+
and bounds the fetch, so a small limit is cheap; it does not mean page size.
|
|
415
|
+
|
|
416
|
+
## Secrets and redaction
|
|
417
|
+
|
|
418
|
+
- **`agent-tokens create` returns a ~776-character token exactly once.** An
|
|
419
|
+
immediate GET returns only `id` and `name`. It is surfaced solely under
|
|
420
|
+
`-o json`, redacted in tables, with a stderr warning that it cannot be
|
|
421
|
+
retrieved again. Capture it at creation or delete and recreate.
|
|
422
|
+
- **Webhook header values are redacted by header NAME, not per hook.**
|
|
423
|
+
`Authorization`, `X-Api-Key`, `api-key`, `X-Secret`, `Token` read back as
|
|
424
|
+
`***`; `Cookie` and arbitrary names come back verbatim. **A GET-edit-PUT
|
|
425
|
+
round trip would store the literal `***` over a real secret**, which is why
|
|
426
|
+
neither webhook family offers `--file` and update sends only your flags.
|
|
427
|
+
|
|
428
|
+
## Tenant-shaped gotchas (not CLI bugs)
|
|
429
|
+
|
|
430
|
+
- **A policy has no name or description** — the body is only
|
|
431
|
+
`inGroups`/`bundles`/`roles`/`sortOrder`, so policies are trackable by UUID
|
|
432
|
+
alone. `POST` also rejects an empty grant (`"Policy must grant at least 1
|
|
433
|
+
permission"`), so a policy can never be created inert: any create is a real
|
|
434
|
+
grant for as long as it lives.
|
|
435
|
+
- **Webhooks cannot be created disabled** in either family, and `/disable`
|
|
436
|
+
404s. `DELETE` is the only off switch — which is why `audit-webhooks` has
|
|
437
|
+
`enable` and deliberately no `--disable` flag.
|
|
438
|
+
- **Deleted webhook names stay reserved**: recreating a just-deleted name 400s
|
|
439
|
+
with "Resource already exists".
|
|
440
|
+
- **`chats channels`** needs the *user* token (the org token 401s) and accepts
|
|
441
|
+
only `--integration-type slack`. It returns
|
|
442
|
+
`500 chatsService.missingScopes` until the tenant's Slack app is
|
|
443
|
+
re-authorized with `channels:read,groups:read,mpim:read,im:read`; the CLI
|
|
444
|
+
exits 3 with that message rather than a generic failure.
|
|
445
|
+
- **`orgRoles` is unreachable** — `GET /orgRoles` and
|
|
446
|
+
`PUT /orgRoles/identities` are 401 on *both* tokens, the same org-level
|
|
447
|
+
gating Pathfinder has. These are the only two API operations the CLI does not
|
|
448
|
+
implement, and they stay unimplemented until the gateway opens up.
|
|
449
|
+
- **Bundle create needs at least one role** (`"roles must contain at least 1
|
|
450
|
+
elements"`), and **role create** needs only `name` + `resource` +
|
|
451
|
+
`requestable` — the spec's required `prerequisitePermissions` is the field
|
|
452
|
+
the server discards. Virtual Application resources also need
|
|
453
|
+
`virtualizedRole`.
|
|
454
|
+
- **Omitting either side of a `usersAccounts` body is a 500, not a 400.**
|
|
455
|
+
|
|
299
456
|
## API Notes
|
|
300
457
|
|
|
301
458
|
- Base path: `/public/v1`
|
|
302
|
-
- Pagination: `page`/`perPage
|
|
303
|
-
|
|
459
|
+
- Pagination: `page`/`perPage`, but only where the response carries a
|
|
460
|
+
`pagination` key — see the table above. `perPage` caps at 100.
|
|
461
|
+
- Response: `{"result": [...]}`, with `pagination` alongside it on the
|
|
462
|
+
endpoints that really page. Single GETs wrap the object in `result` too;
|
|
463
|
+
`eligibleJitAccess` is the exception, returning `{roles, bundles, pagination}`.
|
|
304
464
|
- All IDs are UUIDs (strings)
|
|
305
|
-
- Bearer token auth
|
|
465
|
+
- Bearer token auth; some routes need the *user* token, not the org one
|
|
466
|
+
- Coverage: 75 of 77 operations. The two missing are the `orgRoles` pair,
|
|
467
|
+
blocked by org-level gating. `tests/entitle/test_coverage.py` fails if that
|
|
468
|
+
drifts.
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pf
|
|
3
|
+
description: Pathfinder platform commands — whoami, site/product access, PAT/MCP token listing, machine registry, user invites and product/site access grants, workload identities (OIDC trust for CI/pods/apps) and Cedar IAM policies that grant access to Workload Credentials. Use when working with the Pathfinder portal API (Nomine), platform PATs, site IDs, user onboarding, machine identities, workload identities, or IAM/RBAC policies.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pathfinder Platform (`bt pf`)
|
|
7
|
+
|
|
8
|
+
The Pathfinder platform API (internal name: **Nomine**) manages the BeyondTrust
|
|
9
|
+
cloud portal layer: users, sites, product access, tokens, and machine
|
|
10
|
+
identities, plus the auth service's **workload identities** and the **IAM
|
|
11
|
+
policy service** (Cedar RBAC). `bt pf` talks to it through the public API
|
|
12
|
+
gateway with PAT auth — the same pattern as `bt wlc` / `bt epml`.
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
export BT_PF_API_URL=https://api.beyondtrust.io # default
|
|
18
|
+
export BT_PF_SITE_ID=<site-uuid> # required — the site the PAT was minted on
|
|
19
|
+
export BT_PF_PAT=PAT_xxx # required (mint at app.beyondtrust.io → Manage Profile)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Site ID discovery: log into app.beyondtrust.io, choose your site, then browse
|
|
23
|
+
to `https://app.beyondtrust.io/api/platform/currentSite`.
|
|
24
|
+
|
|
25
|
+
**For `bt pf policies` and `bt pf wi`, point `pf` at the Administration site**:
|
|
26
|
+
the **org id doubles as the Administration site id**, and the PAT must be
|
|
27
|
+
minted there. The policy commands also need the **WLC site id** for Cedar's
|
|
28
|
+
`@siteId` — taken from the `wlc` config (`BT_WLC_SITE_ID`), overridable with
|
|
29
|
+
`--site`. Two sites, two PATs, because PATs are site-bound.
|
|
30
|
+
|
|
31
|
+
## URL mapping (the thing everyone gets wrong)
|
|
32
|
+
|
|
33
|
+
The Nomine swagger writes paths as `/api/auth/X`. On the gateway the product
|
|
34
|
+
segment is `platform` and the `auth/` prefix stays:
|
|
35
|
+
|
|
36
|
+
spec /api/auth/UserInfo → https://api.beyondtrust.io/site/<site-id>/platform/auth/UserInfo
|
|
37
|
+
|
|
38
|
+
**PATs are site-bound**: minted while a site is active in the portal, valid
|
|
39
|
+
only for that site's `/site/<id>/...` paths. Any other site → `401 Access
|
|
40
|
+
denied for this site`. A just-minted PAT can take ~1s to propagate
|
|
41
|
+
(transient `401 Personal access token not found` — retry once).
|
|
42
|
+
|
|
43
|
+
## Commands
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bt pf auth test # connectivity + PAT validity
|
|
47
|
+
bt pf whoami # UserInfo for the PAT owner
|
|
48
|
+
bt pf access # site + product access rules (also -o json)
|
|
49
|
+
bt pf pats list # PATs (values masked; --show-tokens to reveal)
|
|
50
|
+
bt pf mcp-tokens list # MCP tokens (same masking)
|
|
51
|
+
bt pf machines list # registered machine IDs
|
|
52
|
+
bt pf machines get <uuid> # one machine's record (secret hash never shown)
|
|
53
|
+
bt pf wi list # workload identities (alias of `bt pf workload-identities`)
|
|
54
|
+
bt pf policies list # Cedar IAM policies (deleted ones hidden; --all shows them)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## User onboarding & access assignment (`bt pf users`)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Invite a user to the org (sends a real email; confirms unless --yes).
|
|
61
|
+
# Default site = BT_PF_SITE_ID; -p grants products on that site at invite time.
|
|
62
|
+
bt pf users invite -e new.user@corp.com --first-name New --last-name User \
|
|
63
|
+
-p pra -p epml
|
|
64
|
+
|
|
65
|
+
bt pf users cancel-invite new.user@corp.com # cancel a pending invite
|
|
66
|
+
|
|
67
|
+
# Per-user product access on a site (product slugs are lowercase:
|
|
68
|
+
# epmwm, pra, psc, rs, entitle, epml, insights, secrets)
|
|
69
|
+
bt pf users grant -u <user-uuid> -p epml # default site
|
|
70
|
+
bt pf users revoke -u <user-uuid> -p epml -s <site-uuid>
|
|
71
|
+
|
|
72
|
+
# Site membership (optionally granting products in the same call)
|
|
73
|
+
bt pf users add-to-site -u <user-uuid> -s <site-uuid> -p pra -p psc
|
|
74
|
+
bt pf users remove-from-site -u <user-uuid> -s <site-uuid>
|
|
75
|
+
|
|
76
|
+
bt pf users delete <user-uuid> [...] # bulk delete (org admin; confirms)
|
|
77
|
+
bt pf users list # every page (admin-site PAT with 'admin' scope); --page N for one
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**PAT matters (re-verified 2026-10-21):** the org-admin surface answers only a
|
|
81
|
+
PAT minted on the **Administration site** (`BT_PF_SITE_ID` = the org id) that
|
|
82
|
+
carries the **`admin`** scope. With that PAT, `auth/Users`, `auth/SAMLProviders`,
|
|
83
|
+
`auth/Auditing` and `auth/{productaccess,siteaccess,access}/org` are all **200**.
|
|
84
|
+
A product-site PAT still gets the empty-body 403 on every one of them, and that
|
|
85
|
+
is what every PAT we had on 2026-06-11 got, which is why these commands looked
|
|
86
|
+
permanently blocked. Every Administration-site command (`users`, `policies`, `wi`)
|
|
87
|
+
shares one contract: an empty-body 403 (the wrong kind of PAT) exits **3** with a
|
|
88
|
+
hint naming the fix; a 401, or a 403 with a body, exits 1.
|
|
89
|
+
|
|
90
|
+
Writes work too, with the admin-scoped PAT (2026-10-21):
|
|
91
|
+
- `bt pf users delete` removes the Pathfinder user record only, never the IdP
|
|
92
|
+
(Entra ID etc.) account.
|
|
93
|
+
- `bt pf users invite -s <site> -p <slug>...` → `{inviteId, userId,
|
|
94
|
+
organizationId, email, isFederated}`. The user then lists as `status:
|
|
95
|
+
Invited`, `active: false`, with a **new** user id. A re-invited former SAML
|
|
96
|
+
user came back with `isFederated: false`.
|
|
97
|
+
|
|
98
|
+
- `bt pf users add-to-site` / `remove-from-site` / `grant` / `revoke` all work
|
|
99
|
+
(live round trip on a test user, 2026-10-21, net zero). Order matters:
|
|
100
|
+
- **Site first:** a product grant on a site the user isn't a member of →
|
|
101
|
+
`404 "User does not have site access"`. Run `add-to-site` first.
|
|
102
|
+
- **Revoking a rule that doesn't exist** → `500 "Unable to remove product
|
|
103
|
+
access rule."` (exit 1), not a 404.
|
|
104
|
+
- **Reading access back:** site membership is visible through `users list
|
|
105
|
+
--site-id <site>` (`auth/Users?siteId=`). Product rules aren't:
|
|
106
|
+
`auth/productaccess?userId=` is in the swagger but 404s on the gateway.
|
|
107
|
+
- **A pending invite creates no site rule** until it's accepted, so an invitee
|
|
108
|
+
doesn't show in `users list --site-id` for the invited site.
|
|
109
|
+
- `cancel-invite` removes the invitation, but the user record keeps
|
|
110
|
+
`status: Invited`, so the list can't tell a live invite from a cancelled one.
|
|
111
|
+
Re-inviting the same email **reuses that user id** and sends a fresh email.
|
|
112
|
+
After a full `delete`, the next invite gets a new id.
|
|
113
|
+
- The org's *default* access rules (`auth/{site,product}access/org`) may grant
|
|
114
|
+
products to every user anyway. `invite` sends a
|
|
115
|
+
real email. Note `invite`'s default site is `BT_PF_SITE_ID`, which for these
|
|
116
|
+
commands is the Administration site, so pass `-s <product-site>` to grant
|
|
117
|
+
products on a real site.
|
|
118
|
+
|
|
119
|
+
`users list` pages for you: without paging params the server returns a bare
|
|
120
|
+
array **capped at 200** (no total), and with `page`/`pageSize` it returns
|
|
121
|
+
`{users, totalCount}`. The CLI asks for 1000 per page, walks pages to
|
|
122
|
+
`totalCount`, de-duplicates by id, and warns if it comes up short.
|
|
123
|
+
`--page N` fetches just that page.
|
|
124
|
+
|
|
125
|
+
## Workload identities (`bt pf workload-identities`, alias `bt pf wi`)
|
|
126
|
+
|
|
127
|
+
OIDC issuer trust configs for non-human callers (CI jobs, k8s pods, Azure
|
|
128
|
+
apps). Administration site, admin PAT. An identity is granted **nothing**
|
|
129
|
+
until a policy names it.
|
|
130
|
+
|
|
131
|
+
| Command | Method & path |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `list [--wlc] [--for-site S]` | `GET /site/{admin}/platform/auth/workload-identities` → `{issuers: [...], totalCount}` |
|
|
134
|
+
| `get <id>` | `GET .../workload-identities/{id}` → `{issuer: {...}}` — CLI also lists the live policies naming it |
|
|
135
|
+
| `create` | `POST .../workload-identities` → `{issuer}` |
|
|
136
|
+
| `update <id>` | `PUT .../workload-identities/{id}` — full body, read-modify-write |
|
|
137
|
+
| `delete <id>` | `DELETE .../workload-identities/{id}` (confirms; `--yes`) |
|
|
138
|
+
|
|
139
|
+
Item keys (live): `conditions, description, expectedAud, identityId,
|
|
140
|
+
idpCategory, issuerUrl, organizationId, registeredScopes, scopeLevel,
|
|
141
|
+
serviceName, siteId, sites` (`sites[]` = `{siteId, registeredScopes,
|
|
142
|
+
scopeLevel, expectedAud}`).
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
bt pf wi create -n k8s-wc-reader --issuer-url https://kubernetes.default.svc --idp-category Custom \
|
|
146
|
+
-c sub=system:serviceaccount:demo-app:wc-reader --scope secrets --dry-run
|
|
147
|
+
bt pf wi create -n gh-deploy --issuer-url https://token.actions.githubusercontent.com \
|
|
148
|
+
--idp-category GitHubActions -c "sub=repo:owner/repo:*" -c ref=refs/heads/main
|
|
149
|
+
bt pf wi update <id> --description "new" # -c / --conditions-file REPLACE all conditions
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Rules checked client-side (ported from the Terraform provider):
|
|
153
|
+
`idpCategory` ∈ GitHubActions | AzureEntra | Custom; ≥ 1 registered scope
|
|
154
|
+
(`secrets` = WLC, default; `psc`, `admin` also seen); condition values
|
|
155
|
+
non-empty, ≤ 256 chars, at most one `*` and only at the end; GitHubActions
|
|
156
|
+
keys ∈ {sub, repository, repository_owner, ref, job_workflow_ref,
|
|
157
|
+
workflow_ref, environment, event_name, actor} and `sub` must start
|
|
158
|
+
`repo:<owner>/<repo>`. Only `conditions`, `description`, `scopeLevel`,
|
|
159
|
+
`registeredScopes` are updatable; the service lower-cases `serviceName` and
|
|
160
|
+
`issuerUrl`.
|
|
161
|
+
|
|
162
|
+
## IAM policies (`bt pf policies`) — Cedar RBAC for Workload Credentials
|
|
163
|
+
|
|
164
|
+
| Command | Method & path (admin site) |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `list [--all]` | `GET /site/{admin}/platform/iam/policies` → `{policies: [...]}`; DELETED / PENDING_UNBIND hidden unless `--all` |
|
|
167
|
+
| `get <name>` | `GET .../iam/policies/{name}` |
|
|
168
|
+
| `apply <name> -f p.cedar` / `--stdin` | `PUT .../iam/policies/{name}`, **`Content-Type: text/plain`**, raw Cedar — upsert |
|
|
169
|
+
| `grant ...` | build Cedar from flags, show it, confirm (says REPLACE if the name is live), PUT, wait |
|
|
170
|
+
| `render ...` | same flags, print Cedar (`--curl` for the curl). No write |
|
|
171
|
+
| `validate -f p.cedar` | local checks only |
|
|
172
|
+
| `delete <name>` | `DELETE .../iam/policies/{name}` — soft delete (then listed as DELETED) |
|
|
173
|
+
| `wait <name>` | poll until ACTIVE / WAITING_FOR_RESOURCE (`--timeout`) |
|
|
174
|
+
|
|
175
|
+
Policy object: `name, cedar, status, product, site, createdAt, updatedAt`
|
|
176
|
+
(no id, no description). No pagination (query params ignored, 2026-10-21).
|
|
177
|
+
|
|
178
|
+
Cedar the service accepts — one `permit`, `@siteId` = the site the grant
|
|
179
|
+
applies to (the **WLC** site, not the admin site):
|
|
180
|
+
|
|
181
|
+
```cedar
|
|
182
|
+
@siteId("<wlc-site-uuid>")
|
|
183
|
+
permit(
|
|
184
|
+
principal == Pathfinder::Workload::Id::"<identityId>", // or User::Email / Group / Role
|
|
185
|
+
action in [WorkloadCredentials::Action::"ReadSecret", WorkloadCredentials::Action::"ListFolderContents"],
|
|
186
|
+
resource == WorkloadCredentials::Folder::"/btlab/app/demo" // or Secret / DynamicSecret / Integration, or `resource is WorkloadCredentials::Product`
|
|
187
|
+
);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Not supported: `forbid`, `when`, `unless`, multiple statements, missing
|
|
191
|
+
`@siteId`. Roles are written as actions (`WorkloadCredentials::Action::"Viewer"`,
|
|
192
|
+
`Pathfinder::Action::"Admin"`). Full action catalog, role expansion and the
|
|
193
|
+
folder cascade are in the `/wlc` skill.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Viewer for k8s-wc-reader on a folder (cascades to everything below)
|
|
197
|
+
bt pf policies grant --name k8s-wc-reader-demo-viewer --workload-name k8s-wc-reader \
|
|
198
|
+
--folder /btlab/app/demo --role-grant Viewer
|
|
199
|
+
# ReadSecret on one secret
|
|
200
|
+
bt pf policies grant --name k8s-db-pass --workload <id> --secret /btlab/app/demo/db-pass \
|
|
201
|
+
--action ReadSecret --action ReadSecretMetadata
|
|
202
|
+
# Revoke
|
|
203
|
+
bt pf policies delete k8s-db-pass
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Gotchas (live-verified 2026-10-20/21):
|
|
207
|
+
- The **docs' path `/platform/iam/policies` is wrong** — it is
|
|
208
|
+
`/site/{admin-site}/platform/iam/policies`.
|
|
209
|
+
- The list **includes soft-deleted** policies — history, never grants.
|
|
210
|
+
- **PUT is an upsert keyed by name** (unique per org). Reusing a name silently
|
|
211
|
+
replaces it; `grant`/`apply` say REPLACE and show the current Cedar. A
|
|
212
|
+
DELETED name is reusable.
|
|
213
|
+
- **A wrong workload id still goes ACTIVE** (the service doesn't validate
|
|
214
|
+
principals) and grants nothing — `grant` warns.
|
|
215
|
+
- **A missing path → WAITING_FOR_RESOURCE**, which binds when it's created.
|
|
216
|
+
- Statuses: `RESOURCE_LOOKUP`, `PENDING_BIND` (settling) → `ACTIVE`;
|
|
217
|
+
`WAITING_FOR_RESOURCE`; `RESOURCE_LOOKUP_ERROR`, `NO_PRODUCT_ACCESS`
|
|
218
|
+
(failed); `PENDING_UNBIND` → `DELETED`.
|
|
219
|
+
- Names: 1-100 chars, letters/digits/`-`/`_`, alnum at both ends (the
|
|
220
|
+
provider's server-mirrored rule — no dots).
|
|
221
|
+
- A non-admin-site PAT on the policy API → `403 forbidden: caller cannot
|
|
222
|
+
perform this action`.
|
|
223
|
+
|
|
224
|
+
## Groups and roles — known gap
|
|
225
|
+
|
|
226
|
+
Policies can name `Pathfinder::Group::"name"` and `Pathfinder::Role::"name"`,
|
|
227
|
+
but **group and role membership is not visible to PAT callers, so access
|
|
228
|
+
through groups can't be resolved.** There is no groups/roles API in the Nomine
|
|
229
|
+
swagger or the Terraform provider, and read-only probes on the Administration
|
|
230
|
+
site (2026-10-21) found none: `platform/auth/{groups,Groups,roles,Roles,UserGroups}`
|
|
231
|
+
and `platform/iam/{groups,Groups,roles}` → 404; `platform/auth/User/Groups` →
|
|
232
|
+
405 (a route exists but not for GET; not probed further). The only membership
|
|
233
|
+
data a PAT sees is the caller's own `groups` in `bt pf whoami`. `bt wlc access`
|
|
234
|
+
reports group/role grants as "membership not resolved", and always notes that
|
|
235
|
+
Admin-console role assignments can make effective access larger than policies show.
|
|
236
|
+
|
|
237
|
+
## What works over PAT vs what doesn't (re-verified 2026-10-21)
|
|
238
|
+
|
|
239
|
+
| Endpoint | Product-site PAT | Administration-site PAT (`admin` scope) |
|
|
240
|
+
|----------|------------------|------------------------------------------|
|
|
241
|
+
| `auth/UserInfo`, `auth/User/Access`, `auth/User/Token`, `auth/User/McpToken` | 200 | 200 |
|
|
242
|
+
| `auth/Users`, `auth/SAMLProviders`, `auth/Auditing` | 403 (empty body) | **200** |
|
|
243
|
+
| `auth/productaccess/org`, `auth/access/org`, `auth/siteaccess/org` | 403 (empty body) | **200** |
|
|
244
|
+
| `auth/workload-identities`, `iam/policies` | 403 | **200** |
|
|
245
|
+
| `auth/Machine`, `auth/Machine/{id}` | **200** | 403 |
|
|
246
|
+
| `auth/productaccess` (collection GET) | 404 (not deployed) | n/a |
|
|
247
|
+
| `auth/Users/delete` | 405 on GET; POST → 403 (2026-06-11) | **POST works** (deleted a test user, 2026-10-21) |
|
|
248
|
+
| `auth/UserInvitation` | 405 on GET; POST → 403 (2026-06-11) | **POST works**: invited a user to a product site with products, 2026-10-21 |
|
|
249
|
+
| `auth/UserInvitation/Cancel` | 405 on GET | **POST works** (2026-10-21); a second cancel → `404 "User invitation not found."` |
|
|
250
|
+
| `auth/UserInvitation/Product` | 405 on GET | 405 on GET; POST not yet tested |
|
|
251
|
+
| `auth/siteaccess/{s}/user/{u}` | 405 on GET | **POST and DELETE work** (2026-10-21) |
|
|
252
|
+
| `auth/productaccess/{p}/site/{s}/user/{u}` | 405 on GET; grant POST → empty-body 403 (2026-06-11) | **POST and DELETE work** (2026-10-21) |
|
|
253
|
+
| `auth/productaccess?userId=` (read a user's product rules, in the swagger) | 404 | 404: not deployed, so a user's product grants can't be read back |
|
|
254
|
+
|
|
255
|
+
PAT `access` scopes are listed in `bt pf pats list`. The Administration-site PAT
|
|
256
|
+
in use on 2026-10-21 carried `admin` plus every product slug. Note that machines
|
|
257
|
+
work the other way round: they need a product-site PAT.
|
|
258
|
+
|
|
259
|
+
## Machines
|
|
260
|
+
|
|
261
|
+
Platform machine identities: non-user principals (machineId + machineSecret)
|
|
262
|
+
used by agents/integrations to authenticate. `POST
|
|
263
|
+
login.beyondtrust.io/bt.identity/Machine/AccessToken {machineId,
|
|
264
|
+
machineSecret}` → 30-min JWT (machineEndpoints.* scopes only). Records carry
|
|
265
|
+
an activation-token lifecycle (created/expiration) and `active` flag —
|
|
266
|
+
an entry with `active: false` and a pending activation token was registered
|
|
267
|
+
but never activated.
|
|
268
|
+
|
|
269
|
+
## Not exposed via PAT (cookie-auth only, app.beyondtrust.io/api/auth/*)
|
|
270
|
+
|
|
271
|
+
User listing, auditing, SAML providers and the org-default access-rule reads
|
|
272
|
+
now work over an Administration-site PAT with the `admin` scope (see above).
|
|
273
|
+
Still cookie-only as far as tested: PAT/MCP token creation and revocation, and
|
|
274
|
+
invitation Accept. These need the `access` session cookie
|
|
275
|
+
(browser JWT ~5min TTL, or the programmatic SignIn → /session/login flow —
|
|
276
|
+
password-auth users only, not federated).
|
|
277
|
+
|
|
278
|
+
Invite body shape (spec `InviteOrganizationUserInput`): `{email, firstName,
|
|
279
|
+
lastName, siteMetaData: [{siteId, name, residency}], productMetaData:
|
|
280
|
+
[{name, siteId, siteName}]}` — `bt pf users invite` builds this, defaulting
|
|
281
|
+
the site to `BT_PF_SITE_ID` and omitting name/residency unless given.
|
|
@@ -14,6 +14,9 @@ description: Privileged Remote Access commands for jump items, vault accounts, a
|
|
|
14
14
|
- `bt pra jump-items tunnel delete` - Deletes protocol tunnel
|
|
15
15
|
- `bt pra jump-groups delete` - Deletes jump group
|
|
16
16
|
- `bt pra vault accounts delete` - Deletes vault account
|
|
17
|
+
- `bt pra users delete` - Deletes a user (non-admins only)
|
|
18
|
+
- `bt pra policies group delete` - Deletes a Group Policy
|
|
19
|
+
- `bt pra policies group {jumpoints|members|jump-groups|vault-accounts|vault-account-groups|teams} remove` - Detaches a sub-resource from a Group Policy
|
|
17
20
|
|
|
18
21
|
List affected resources first, then ask for explicit confirmation.
|
|
19
22
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: secrets
|
|
3
|
+
description: Old name for Workload Credentials (WLC). `bt secrets` is a deprecated alias of `bt wlc` — use the /wlc skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Secrets API → Workload Credentials (`bt wlc`)
|
|
7
|
+
|
|
8
|
+
BeyondTrust renamed the Secrets API (Secrets Safe) to **Workload Credentials
|
|
9
|
+
(WLC)**. In bt-cli 0.6.0 the commands moved to **`bt wlc`**; `bt secrets` is a
|
|
10
|
+
deprecated alias with the same commands (it prints a one-line notice on
|
|
11
|
+
stderr, so `-o json` output is unchanged).
|
|
12
|
+
|
|
13
|
+
- Commands: `bt wlc folders|static|dynamic|leases|integrations|auth|tree|access`
|
|
14
|
+
- Config: `BT_WLC_*` (falls back to `BT_SECRETS_*`), `wlc` profile section (falls back to `secrets`)
|
|
15
|
+
- URL prefix: `/site/{id}/wlc` (`/site/{id}/secrets` still answers)
|
|
16
|
+
- Access policies & workload identities: `bt pf policies`, `bt pf wi`
|
|
17
|
+
|
|
18
|
+
**Use the `/wlc` skill** — it has everything that used to be here, plus the
|
|
19
|
+
Cedar policy and effective-access material.
|