dockerhub-api 1.3.9__tar.gz → 1.4.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.
Files changed (93) hide show
  1. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/PKG-INFO +11 -17
  2. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/README.md +9 -15
  3. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_access_tokens.py +3 -1
  4. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_audit_logs.py +3 -3
  5. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_base.py +1 -20
  6. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_groups.py +4 -2
  7. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_org_access_tokens.py +3 -1
  8. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_orgs.py +5 -3
  9. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_registry.py +11 -9
  10. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_registry_base.py +0 -2
  11. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/auth.py +4 -17
  12. dockerhub_api-1.4.0/dockerhub_api/connector_contracts.py +961 -0
  13. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/kg_ingest.py +3 -3
  14. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/mcp/__init__.py +2 -49
  15. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/mcp/mcp_admin.py +1 -1
  16. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/mcp/mcp_kg.py +2 -2
  17. dockerhub_api-1.4.0/dockerhub_api/mcp_server.py +26 -0
  18. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/ontology/certification.json +3 -3
  19. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/ontology/dockerhub.ttl +7 -12
  20. dockerhub_api-1.4.0/dockerhub_api/ontology/shapes/connector.shacl.ttl +64 -0
  21. dockerhub_api-1.4.0/dockerhub_api/prompts/dockerhub-mcp-operator.json +277 -0
  22. dockerhub_api-1.4.0/dockerhub_api/prompts/dockerhub-mcp-organization-administrator.json +184 -0
  23. dockerhub_api-1.4.0/dockerhub_api/prompts/dockerhub-mcp-release-engineer.json +156 -0
  24. dockerhub_api-1.4.0/dockerhub_api/prompts/dockerhub-mcp-security-analyst.json +123 -0
  25. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/skills/dockerhub-api-development/SKILL.md +5 -5
  26. dockerhub_api-1.4.0/dockerhub_api/skills/dockerhub-api-operations/SKILL.md +74 -0
  27. dockerhub_api-1.4.0/dockerhub_api/skills/dockerhub-image-registry-scout/SKILL.md +62 -0
  28. dockerhub_api-1.4.0/dockerhub_api/skills/dockerhub-org-administration/SKILL.md +109 -0
  29. dockerhub_api-1.4.0/dockerhub_api/skills/dockerhub-repository-management/SKILL.md +56 -0
  30. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api.egg-info/PKG-INFO +11 -17
  31. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api.egg-info/SOURCES.txt +6 -13
  32. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api.egg-info/requires.txt +1 -1
  33. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/pyproject.toml +2 -2
  34. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_audit.py +1 -1
  35. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_org.py +6 -16
  36. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_registry.py +11 -24
  37. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_teams.py +4 -9
  38. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_tokens.py +4 -12
  39. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_wrapper.py +2 -8
  40. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_auth.py +0 -6
  41. dockerhub_api-1.4.0/tests/test_destructive_declaration.py +98 -0
  42. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_kg_ingest.py +3 -3
  43. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_audit.py +0 -44
  44. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_auth.py +0 -66
  45. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_org.py +0 -58
  46. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_registry.py +0 -80
  47. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_repos.py +0 -58
  48. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_scim.py +0 -56
  49. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_scout.py +0 -60
  50. dockerhub_api-1.3.9/dockerhub_api/mcp/mcp_teams.py +0 -55
  51. dockerhub_api-1.3.9/dockerhub_api/mcp_server.py +0 -44
  52. dockerhub_api-1.3.9/dockerhub_api/ontology/shapes/connector.shacl.ttl +0 -144
  53. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-api-operations/SKILL.md +0 -33
  54. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-api-operations/agents/openai.yaml +0 -4
  55. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-api-operations/references/catalog.md +0 -7
  56. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-image-registry-scout/SKILL.md +0 -107
  57. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-image-registry-scout/WORKFLOW.md +0 -93
  58. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-org-administration/SKILL.md +0 -105
  59. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-org-administration/WORKFLOW.md +0 -91
  60. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-repository-management/SKILL.md +0 -111
  61. dockerhub_api-1.3.9/dockerhub_api/skills/dockerhub-repository-management/WORKFLOW.md +0 -97
  62. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/LICENSE +0 -0
  63. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/__init__.py +0 -0
  64. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/__main__.py +0 -0
  65. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/__init__.py +0 -0
  66. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_auth.py +0 -0
  67. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_repositories.py +0 -0
  68. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_scim.py +0 -0
  69. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api/api_client_scout.py +0 -0
  70. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/api_client.py +0 -0
  71. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/connector_sources.py +0 -0
  72. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/dockerhub_input_models.py +0 -0
  73. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/dockerhub_response_models.py +0 -0
  74. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/mcp_config.json +0 -0
  75. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/ontology/__init__.py +0 -0
  76. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/ontology/fixtures/records.json +0 -0
  77. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/ontology/mappings/source.yaml +0 -0
  78. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api/ontology/migrations/manifest.json +0 -0
  79. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api.egg-info/dependency_links.txt +0 -0
  80. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api.egg-info/entry_points.txt +0 -0
  81. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/dockerhub_api.egg-info/top_level.txt +0 -0
  82. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/setup.cfg +0 -0
  83. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_repositories.py +0 -0
  84. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_scim.py +0 -0
  85. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_api_scout.py +0 -0
  86. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_dockerhub_a2a_validation.py +0 -0
  87. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_dockerhub_mcp_validation.py +0 -0
  88. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_fleet_name.py +0 -0
  89. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_identity_namespace_scoping.py +0 -0
  90. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_init_dynamics.py +0 -0
  91. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_models.py +0 -0
  92. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_rate_limit.py +0 -0
  93. {dockerhub_api-1.3.9 → dockerhub_api-1.4.0}/tests/test_startup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dockerhub-api
3
- Version: 1.3.9
3
+ Version: 1.4.0
4
4
  Summary: Docker Hub API + MCP Server
5
5
  Author-email: Repository Maintainers <maintainers@knuckles.team>
6
6
  License: MIT
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: <3.15,>=3.12
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: scion-sdk[mcp]<0.7.0,>=0.6.4
15
+ Requires-Dist: scion-sdk[mcp]<0.11,>=0.10.2
16
16
  Requires-Dist: httpx>=0.28.1
17
17
  Requires-Dist: python-dotenv>=1.2.4
18
18
  Provides-Extra: test
@@ -84,8 +84,7 @@ multi-arch inspection, OCI referrers, and gated push/remove) and **Docker Scout*
84
84
  expiry, with one transparent re-mint on 401.
85
85
  - **Rate-Limit Telemetry:** `X-RateLimit-*` headers surfaced in every result;
86
86
  HTTP 429 retried with bounded `Retry-After` backoff.
87
- - **Safety by Default:** Removes and org-settings writes are gated behind
88
- `DOCKERHUB_ALLOW_DESTRUCTIVE` (default `False`); secrets are redacted from tool
87
+ - **Safety by Default:** Removes and org-settings writes are declared destructive; the SDK asks the caller to confirm (`confirm=true`) before any request. Secrets are redacted from tool
89
88
  results (plaintext tokens appear exactly once — on creation). Repository creation
90
89
  stays enabled: it is the primary release-provisioning use case.
91
90
  - **Integrated Graph Agent:** Built-in Pydantic AI agent (`dockerhub-agent`) with
@@ -126,7 +125,7 @@ One multi-stage `docker/Dockerfile` builds the MCP-only image (`--target mcp`):
126
125
 
127
126
  | Image tag | Build target | Contents | Entrypoint |
128
127
  |-----------|--------------|----------|------------|
129
- | `example/dockerhub-api:mcp` | `--target mcp` | `dockerhub-api[mcp]` — **connector-focused**, includes `epistemic-graph[full]`; no model-orchestration stack | `dockerhub-mcp` |
128
+ | `example/dockerhub-api:mcp` | `--target mcp` | `dockerhub-api[mcp]` — **connector-focused**, includes `medulla`; no model-orchestration stack | `dockerhub-mcp` |
130
129
 
131
130
  ```bash
132
131
  docker build --target mcp -t example/dockerhub-api:mcp docker/ # connector-focused MCP server
@@ -134,14 +133,14 @@ docker build --target mcp -t example/dockerhub-api:mcp docker/ # connecto
134
133
 
135
134
  agent (`immutable agent digest`) with a co-located `:mcp` sidecar.
136
135
 
137
- ### Knowledge-graph database (`epistemic-graph`)
136
+ ### Knowledge-graph database (`medulla`)
138
137
 
139
- Scion SDK core dependency (`epistemic-graph[full]`). The `[mcp]` extra keeps
138
+ Scion SDK core dependency (`medulla`). The `[mcp]` extra keeps
140
139
  deployments can use the bundled engine. For production or shared state, run
141
- **epistemic-graph as a dedicated database service** and configure the runtime to use it.
140
+ **medulla as a dedicated database service** and configure the runtime to use it.
142
141
  Deployment recipes (single-node + Raft HA), connection configuration, and architecture
143
142
  diagrams are documented in the
144
- [epistemic-graph deployment guide](https://knuckles-team.github.io/epistemic-graph/deployment/).
143
+ [medulla deployment guide](https://knuckles-team.github.io/medulla/deployment/).
145
144
 
146
145
  ---
147
146
 
@@ -178,7 +177,7 @@ _Auto-generated — do not edit (synced by the `mcp-readme-table` pre-commit hoo
178
177
  | `hub_admin` | `ADMINTOOL` | Client diagnostics: 'rate_limit' returns the latest |
179
178
  | `hub_audit` | `AUDITTOOL` | Read a Docker Hub account's audit trail: 'logs' lists events |
180
179
  | `hub_auth` | `AUTHTOOL` | Manage Docker Hub authentication, personal access tokens (PATs), |
181
- | `dockerhub_ingest_repositories` | `KGTOOL` | Natively ingest repositories into the epistemic-graph as typed :Repository/:ContainerImage nodes. |
180
+ | `dockerhub_ingest_repositories` | `KGTOOL` | Natively ingest repositories into the medulla as typed :Repository/:ContainerImage nodes. |
182
181
  | `hub_org` | `ORGTOOL` | Manage a Docker Hub organization: settings (restricted images), |
183
182
  | `hub_registry` | `REGISTRYTOOL` | Docker Registry v2 image operations (``registry-1.docker.io``): |
184
183
  | `hub_repos` | `REPOSTOOL` | Manage Docker Hub repositories: list/create/inspect repositories, |
@@ -259,7 +258,7 @@ _9 action-routed tool(s) (default) · 54 verbose 1:1 tool(s). Each is enabled un
259
258
 
260
259
  > **Install the connector-focused `[mcp]` extra.** Examples use `dockerhub-api[mcp]` to add
261
260
  > FastMCP / FastAPI through `scion-sdk`; the required Scion SDK core
262
- > still carries `epistemic-graph[full]`. The `[agent-runtime]` extra additionally
261
+ > still carries `medulla`. The `[agent-runtime]` extra additionally
263
262
  > enables model orchestration.
264
263
 
265
264
  #### stdio Transport (local IDEs — Cursor, Claude Desktop, VS Code)
@@ -279,7 +278,6 @@ _9 action-routed tool(s) (default) · 54 verbose 1:1 tool(s). Each is enabled un
279
278
  "ADMINTOOL": "True",
280
279
  "AUDITTOOL": "True",
281
280
  "AUTHTOOL": "True",
282
- "DOCKERHUB_ALLOW_DESTRUCTIVE": "False",
283
281
  "DOCKERHUB_TLS_PROFILE": "system",
284
282
  "DOCKERHUB_TOKEN": "dckr_pat_your_personal_access_token",
285
283
  "DOCKERHUB_URL": "https://hub.docker.com",
@@ -328,7 +326,6 @@ own runtime secret boundary.
328
326
  "ADMINTOOL": "True",
329
327
  "AUDITTOOL": "True",
330
328
  "AUTHTOOL": "True",
331
- "DOCKERHUB_ALLOW_DESTRUCTIVE": "False",
332
329
  "DOCKERHUB_TLS_PROFILE": "system",
333
330
  "DOCKERHUB_TOKEN": "dckr_pat_your_personal_access_token",
334
331
  "DOCKERHUB_URL": "https://hub.docker.com",
@@ -376,7 +373,6 @@ docker run -i --rm \
376
373
  -e ADMINTOOL=True \
377
374
  -e AUDITTOOL=True \
378
375
  -e AUTHTOOL=True \
379
- -e DOCKERHUB_ALLOW_DESTRUCTIVE=False \
380
376
  -e DOCKERHUB_TLS_PROFILE=system \
381
377
  -e DOCKERHUB_TOKEN=dckr_pat_your_personal_access_token \
382
378
  -e DOCKERHUB_URL=https://hub.docker.com \
@@ -423,8 +419,7 @@ the detailed transport contract.
423
419
  |---|---|---|
424
420
  | Reads (repos, tags, members, logs, SCIM) | allowed | — |
425
421
  | Repository create / immutable-tag config / invites / role updates | allowed | — |
426
- | Removes (PATs, OATs, groups, members, invites) | **blocked** | `DOCKERHUB_ALLOW_DESTRUCTIVE=True` |
427
- | Org-settings writes (`PUT /v2/orgs/{org}/settings`) | **blocked** | `DOCKERHUB_ALLOW_DESTRUCTIVE=True` |
422
+ | Removes (PATs, OATs, groups, members, invites) and org-settings writes | confirmation required | `confirm=true` per call, or operator setting `SCION_ALLOW_DESTRUCTIVE` |
428
423
 
429
424
  ---
430
425
 
@@ -488,7 +483,6 @@ network transport, connector ingestion, Arbora delegation, or trace export.
488
483
  | `DOCKER_REGISTRY_URL` | `https://registry-1.docker.io` | |
489
484
  | `DOCKER_REGISTRY_AUTH_URL` | `https://auth.docker.io/token` | token-service realm; auto-discovered from the 401 challenge |
490
485
  | `DOCKER_SCOUT_URL` | `https://api.scout.docker.com` | |
491
- | `DOCKERHUB_ALLOW_DESTRUCTIVE` | `False` | |
492
486
  | `AUTHTOOL` | `True` | |
493
487
  | `REPOSTOOL` | `True` | |
494
488
  | `ORGTOOL` | `True` | |
@@ -60,8 +60,7 @@ multi-arch inspection, OCI referrers, and gated push/remove) and **Docker Scout*
60
60
  expiry, with one transparent re-mint on 401.
61
61
  - **Rate-Limit Telemetry:** `X-RateLimit-*` headers surfaced in every result;
62
62
  HTTP 429 retried with bounded `Retry-After` backoff.
63
- - **Safety by Default:** Removes and org-settings writes are gated behind
64
- `DOCKERHUB_ALLOW_DESTRUCTIVE` (default `False`); secrets are redacted from tool
63
+ - **Safety by Default:** Removes and org-settings writes are declared destructive; the SDK asks the caller to confirm (`confirm=true`) before any request. Secrets are redacted from tool
65
64
  results (plaintext tokens appear exactly once — on creation). Repository creation
66
65
  stays enabled: it is the primary release-provisioning use case.
67
66
  - **Integrated Graph Agent:** Built-in Pydantic AI agent (`dockerhub-agent`) with
@@ -102,7 +101,7 @@ One multi-stage `docker/Dockerfile` builds the MCP-only image (`--target mcp`):
102
101
 
103
102
  | Image tag | Build target | Contents | Entrypoint |
104
103
  |-----------|--------------|----------|------------|
105
- | `example/dockerhub-api:mcp` | `--target mcp` | `dockerhub-api[mcp]` — **connector-focused**, includes `epistemic-graph[full]`; no model-orchestration stack | `dockerhub-mcp` |
104
+ | `example/dockerhub-api:mcp` | `--target mcp` | `dockerhub-api[mcp]` — **connector-focused**, includes `medulla`; no model-orchestration stack | `dockerhub-mcp` |
106
105
 
107
106
  ```bash
108
107
  docker build --target mcp -t example/dockerhub-api:mcp docker/ # connector-focused MCP server
@@ -110,14 +109,14 @@ docker build --target mcp -t example/dockerhub-api:mcp docker/ # connecto
110
109
 
111
110
  agent (`immutable agent digest`) with a co-located `:mcp` sidecar.
112
111
 
113
- ### Knowledge-graph database (`epistemic-graph`)
112
+ ### Knowledge-graph database (`medulla`)
114
113
 
115
- Scion SDK core dependency (`epistemic-graph[full]`). The `[mcp]` extra keeps
114
+ Scion SDK core dependency (`medulla`). The `[mcp]` extra keeps
116
115
  deployments can use the bundled engine. For production or shared state, run
117
- **epistemic-graph as a dedicated database service** and configure the runtime to use it.
116
+ **medulla as a dedicated database service** and configure the runtime to use it.
118
117
  Deployment recipes (single-node + Raft HA), connection configuration, and architecture
119
118
  diagrams are documented in the
120
- [epistemic-graph deployment guide](https://knuckles-team.github.io/epistemic-graph/deployment/).
119
+ [medulla deployment guide](https://knuckles-team.github.io/medulla/deployment/).
121
120
 
122
121
  ---
123
122
 
@@ -154,7 +153,7 @@ _Auto-generated — do not edit (synced by the `mcp-readme-table` pre-commit hoo
154
153
  | `hub_admin` | `ADMINTOOL` | Client diagnostics: 'rate_limit' returns the latest |
155
154
  | `hub_audit` | `AUDITTOOL` | Read a Docker Hub account's audit trail: 'logs' lists events |
156
155
  | `hub_auth` | `AUTHTOOL` | Manage Docker Hub authentication, personal access tokens (PATs), |
157
- | `dockerhub_ingest_repositories` | `KGTOOL` | Natively ingest repositories into the epistemic-graph as typed :Repository/:ContainerImage nodes. |
156
+ | `dockerhub_ingest_repositories` | `KGTOOL` | Natively ingest repositories into the medulla as typed :Repository/:ContainerImage nodes. |
158
157
  | `hub_org` | `ORGTOOL` | Manage a Docker Hub organization: settings (restricted images), |
159
158
  | `hub_registry` | `REGISTRYTOOL` | Docker Registry v2 image operations (``registry-1.docker.io``): |
160
159
  | `hub_repos` | `REPOSTOOL` | Manage Docker Hub repositories: list/create/inspect repositories, |
@@ -235,7 +234,7 @@ _9 action-routed tool(s) (default) · 54 verbose 1:1 tool(s). Each is enabled un
235
234
 
236
235
  > **Install the connector-focused `[mcp]` extra.** Examples use `dockerhub-api[mcp]` to add
237
236
  > FastMCP / FastAPI through `scion-sdk`; the required Scion SDK core
238
- > still carries `epistemic-graph[full]`. The `[agent-runtime]` extra additionally
237
+ > still carries `medulla`. The `[agent-runtime]` extra additionally
239
238
  > enables model orchestration.
240
239
 
241
240
  #### stdio Transport (local IDEs — Cursor, Claude Desktop, VS Code)
@@ -255,7 +254,6 @@ _9 action-routed tool(s) (default) · 54 verbose 1:1 tool(s). Each is enabled un
255
254
  "ADMINTOOL": "True",
256
255
  "AUDITTOOL": "True",
257
256
  "AUTHTOOL": "True",
258
- "DOCKERHUB_ALLOW_DESTRUCTIVE": "False",
259
257
  "DOCKERHUB_TLS_PROFILE": "system",
260
258
  "DOCKERHUB_TOKEN": "dckr_pat_your_personal_access_token",
261
259
  "DOCKERHUB_URL": "https://hub.docker.com",
@@ -304,7 +302,6 @@ own runtime secret boundary.
304
302
  "ADMINTOOL": "True",
305
303
  "AUDITTOOL": "True",
306
304
  "AUTHTOOL": "True",
307
- "DOCKERHUB_ALLOW_DESTRUCTIVE": "False",
308
305
  "DOCKERHUB_TLS_PROFILE": "system",
309
306
  "DOCKERHUB_TOKEN": "dckr_pat_your_personal_access_token",
310
307
  "DOCKERHUB_URL": "https://hub.docker.com",
@@ -352,7 +349,6 @@ docker run -i --rm \
352
349
  -e ADMINTOOL=True \
353
350
  -e AUDITTOOL=True \
354
351
  -e AUTHTOOL=True \
355
- -e DOCKERHUB_ALLOW_DESTRUCTIVE=False \
356
352
  -e DOCKERHUB_TLS_PROFILE=system \
357
353
  -e DOCKERHUB_TOKEN=dckr_pat_your_personal_access_token \
358
354
  -e DOCKERHUB_URL=https://hub.docker.com \
@@ -399,8 +395,7 @@ the detailed transport contract.
399
395
  |---|---|---|
400
396
  | Reads (repos, tags, members, logs, SCIM) | allowed | — |
401
397
  | Repository create / immutable-tag config / invites / role updates | allowed | — |
402
- | Removes (PATs, OATs, groups, members, invites) | **blocked** | `DOCKERHUB_ALLOW_DESTRUCTIVE=True` |
403
- | Org-settings writes (`PUT /v2/orgs/{org}/settings`) | **blocked** | `DOCKERHUB_ALLOW_DESTRUCTIVE=True` |
398
+ | Removes (PATs, OATs, groups, members, invites) and org-settings writes | confirmation required | `confirm=true` per call, or operator setting `SCION_ALLOW_DESTRUCTIVE` |
404
399
 
405
400
  ---
406
401
 
@@ -464,7 +459,6 @@ network transport, connector ingestion, Arbora delegation, or trace export.
464
459
  | `DOCKER_REGISTRY_URL` | `https://registry-1.docker.io` | |
465
460
  | `DOCKER_REGISTRY_AUTH_URL` | `https://auth.docker.io/token` | token-service realm; auto-discovered from the 401 challenge |
466
461
  | `DOCKER_SCOUT_URL` | `https://api.scout.docker.com` | |
467
- | `DOCKERHUB_ALLOW_DESTRUCTIVE` | `False` | |
468
462
  | `AUTHTOOL` | `True` | |
469
463
  | `REPOSTOOL` | `True` | |
470
464
  | `ORGTOOL` | `True` | |
@@ -5,6 +5,8 @@ CONCEPT:DH-OS.identity.jwt-auth-lifecycle-endpoint — JWT auth lifecycle (PAT m
5
5
 
6
6
  from typing import Any
7
7
 
8
+ from scion_sdk.mcp.destructive import destructive_operation
9
+
8
10
  from dockerhub_api.api.api_client_base import DockerHubApiBase
9
11
  from dockerhub_api.dockerhub_input_models import (
10
12
  AccessTokenCreateModel,
@@ -70,8 +72,8 @@ class DockerHubApiAccessTokens(DockerHubApiBase):
70
72
  envelope["data"] = validate_lenient(AccessToken, envelope["data"])
71
73
  return envelope
72
74
 
75
+ @destructive_operation
73
76
  def delete_access_token(self, uuid: str) -> dict[str, Any]:
74
77
  """Delete a personal access token. Destructive — gated."""
75
- self._guard_destructive("delete_access_token")
76
78
  model = AccessTokenModel(uuid=uuid)
77
79
  return self._request("DELETE", f"/v2/access-tokens/{model.uuid}")
@@ -20,7 +20,7 @@ class DockerHubApiAuditLogs(DockerHubApiBase):
20
20
  def get_audit_logs(
21
21
  self,
22
22
  account: str,
23
- action: str | None = None,
23
+ audit_action: str | None = None,
24
24
  name: str | None = None,
25
25
  actor: str | None = None,
26
26
  from_date: str | None = None,
@@ -30,12 +30,12 @@ class DockerHubApiAuditLogs(DockerHubApiBase):
30
30
  ) -> dict[str, Any]:
31
31
  """List audit-log events for a namespace.
32
32
 
33
- Filters: ``action``, ``name`` (object), ``actor`` (username), and a
33
+ Filters: ``audit_action``, ``name`` (object), ``actor`` (username), and a
34
34
  ``from_date``/``to_date`` RFC 3339 window (sent as ``from``/``to``).
35
35
  """
36
36
  model = AuditLogModel(
37
37
  account=account,
38
- action=action,
38
+ action=audit_action,
39
39
  name=name,
40
40
  actor=actor,
41
41
  from_date=from_date,
@@ -10,8 +10,7 @@ headers are captured into the result envelope (and kept on the client as
10
10
  ``Retry-After`` backoff.
11
11
 
12
12
  CONCEPT:DH-OS.identity.destructive-action-gating-member — destructive-action gating. Deletes and org-settings writes
13
- raise :class:`DestructiveOperationError` unless the client was built with
14
- ``allow_destructive=True``.
13
+ are declared with the SDK ``destructive_operation`` decorator; the SDK asks the caller to confirm.
15
14
  """
16
15
 
17
16
  import logging
@@ -40,10 +39,6 @@ DEFAULT_MAX_RETRIES = 3
40
39
  DEFAULT_TIMEOUT = 30.0
41
40
 
42
41
 
43
- class DestructiveOperationError(PermissionError):
44
- """Raised when a destructive operation is attempted while gated off."""
45
-
46
-
47
42
  class DockerHubApiBase:
48
43
  """Shared transport, auth header, retry, and envelope logic."""
49
44
 
@@ -58,7 +53,6 @@ class DockerHubApiBase:
58
53
  timeout: float = DEFAULT_TIMEOUT,
59
54
  max_retries: int = DEFAULT_MAX_RETRIES,
60
55
  retry_after_cap: float = DEFAULT_RETRY_AFTER_CAP,
61
- allow_destructive: bool = False,
62
56
  debug: bool = False,
63
57
  transport: httpx.BaseTransport | None = None,
64
58
  ):
@@ -75,7 +69,6 @@ class DockerHubApiBase:
75
69
  self.timeout = timeout
76
70
  self.max_retries = max_retries
77
71
  self.retry_after_cap = retry_after_cap
78
- self.allow_destructive = allow_destructive
79
72
  self.debug = debug
80
73
  #: Last rate-limit snapshot seen on any response.
81
74
  self.rate_limit: dict[str, Any] = {}
@@ -188,18 +181,6 @@ class DockerHubApiBase:
188
181
  "rate_limit": dict(self.rate_limit) if self.rate_limit else {},
189
182
  }
190
183
 
191
- # ------------------------------------------------------------------ #
192
- # Destructive gating
193
- # ------------------------------------------------------------------ #
194
-
195
- def _guard_destructive(self, operation: str) -> None:
196
- if not self.allow_destructive:
197
- raise DestructiveOperationError(
198
- f"Destructive operation '{operation}' is disabled. "
199
- "Build the client with allow_destructive=True or set "
200
- "DOCKERHUB_ALLOW_DESTRUCTIVE=True to enable it."
201
- )
202
-
203
184
  # ------------------------------------------------------------------ #
204
185
  # Request engine
205
186
  # ------------------------------------------------------------------ #
@@ -6,6 +6,8 @@ CONCEPT:DH-OS.identity.destructive-action-gating-member — destructive-action g
6
6
 
7
7
  from typing import Any
8
8
 
9
+ from scion_sdk.mcp.destructive import destructive_operation
10
+
9
11
  from dockerhub_api.api.api_client_base import DockerHubApiBase
10
12
  from dockerhub_api.dockerhub_input_models import (
11
13
  GroupCreateModel,
@@ -99,9 +101,9 @@ class DockerHubApiGroups(DockerHubApiBase):
99
101
  envelope["data"] = validate_lenient(Group, envelope["data"])
100
102
  return envelope
101
103
 
104
+ @destructive_operation
102
105
  def delete_group(self, org: str, group_name: str) -> dict[str, Any]:
103
106
  """Delete a group. Destructive — gated."""
104
- self._guard_destructive("delete_group")
105
107
  model = GroupModel(org=org, group_name=group_name)
106
108
  return self._request(
107
109
  "DELETE", f"/v2/orgs/{model.org}/groups/{model.group_name}"
@@ -146,11 +148,11 @@ class DockerHubApiGroups(DockerHubApiBase):
146
148
  envelope["data"] = validate_lenient(OrgMember, envelope["data"])
147
149
  return envelope
148
150
 
151
+ @destructive_operation
149
152
  def remove_group_member(
150
153
  self, org: str, group_name: str, username: str
151
154
  ) -> dict[str, Any]:
152
155
  """Remove a username from a group. Destructive — gated."""
153
- self._guard_destructive("remove_group_member")
154
156
  model = GroupMemberModel(org=org, group_name=group_name, username=username)
155
157
  return self._request(
156
158
  "DELETE",
@@ -5,6 +5,8 @@ CONCEPT:DH-OS.identity.jwt-auth-lifecycle-endpoint — JWT auth lifecycle (OAT m
5
5
 
6
6
  from typing import Any
7
7
 
8
+ from scion_sdk.mcp.destructive import destructive_operation
9
+
8
10
  from dockerhub_api.api.api_client_base import DockerHubApiBase
9
11
  from dockerhub_api.dockerhub_input_models import (
10
12
  OrgAccessTokenCreateModel,
@@ -97,9 +99,9 @@ class DockerHubApiOrgAccessTokens(DockerHubApiBase):
97
99
  envelope["data"] = validate_lenient(OrgAccessToken, envelope["data"])
98
100
  return envelope
99
101
 
102
+ @destructive_operation
100
103
  def delete_org_access_token(self, org: str, token_id: str) -> dict[str, Any]:
101
104
  """Delete an organization access token. Destructive — gated."""
102
- self._guard_destructive("delete_org_access_token")
103
105
  model = OrgAccessTokenModel(org=org, token_id=token_id)
104
106
  return self._request(
105
107
  "DELETE", f"/v2/orgs/{model.org}/access-tokens/{model.token_id}"
@@ -7,6 +7,8 @@ org-settings writes).
7
7
 
8
8
  from typing import Any
9
9
 
10
+ from scion_sdk.mcp.destructive import destructive_operation
11
+
10
12
  from dockerhub_api.api.api_client_base import DockerHubApiBase
11
13
  from dockerhub_api.dockerhub_input_models import (
12
14
  BulkInviteModel,
@@ -37,6 +39,7 @@ class DockerHubApiOrgs(DockerHubApiBase):
37
39
  envelope["data"] = validate_lenient(OrgSettings, envelope["data"])
38
40
  return envelope
39
41
 
42
+ @destructive_operation
40
43
  def update_org_settings(
41
44
  self,
42
45
  org: str,
@@ -50,7 +53,6 @@ class DockerHubApiOrgs(DockerHubApiBase):
50
53
  org-approved images, with carve-outs for Docker Official Images and
51
54
  Verified Publisher images.
52
55
  """
53
- self._guard_destructive("update_org_settings")
54
56
  model = OrgSettingsModel(
55
57
  org=org,
56
58
  restricted_images_enabled=restricted_images_enabled,
@@ -104,9 +106,9 @@ class DockerHubApiOrgs(DockerHubApiBase):
104
106
  envelope["data"] = validate_lenient(OrgMember, envelope["data"])
105
107
  return envelope
106
108
 
109
+ @destructive_operation
107
110
  def remove_org_member(self, org: str, username: str) -> dict[str, Any]:
108
111
  """Remove a member from the organization. Destructive — gated."""
109
- self._guard_destructive("remove_org_member")
110
112
  model = OrgMemberModel(org=org, username=username)
111
113
  return self._request("DELETE", f"/v2/orgs/{model.org}/members/{model.username}")
112
114
 
@@ -118,9 +120,9 @@ class DockerHubApiOrgs(DockerHubApiBase):
118
120
  envelope["data"] = validate_lenient(InvitePage, envelope["data"])
119
121
  return envelope
120
122
 
123
+ @destructive_operation
121
124
  def delete_invite(self, invite_id: str) -> dict[str, Any]:
122
125
  """Cancel an invite. Destructive — gated."""
123
- self._guard_destructive("delete_invite")
124
126
  model = InviteModel(invite_id=invite_id)
125
127
  return self._request("DELETE", f"/v2/invites/{model.invite_id}")
126
128
 
@@ -6,8 +6,8 @@ multi-arch platform resolution, digest resolution, and (gated) deletes.
6
6
  CONCEPT:DH-OS.governance.oci-referrers-attestation-discovery — OCI Referrers / attestation discovery.
7
7
  CONCEPT:DH-OS.governance.chunked-blob-push-upload — chunked blob push (upload session + manifest put).
8
8
 
9
- Pushes, deletes, and blob uploads are destructive and gated by
10
- ``allow_destructive`` (``DOCKERHUB_ALLOW_DESTRUCTIVE``). ``_catalog`` is
9
+ Pushes, deletes, and blob uploads are destructive: the SDK
10
+ asks the caller to confirm each. ``_catalog`` is
11
11
  intentionally omitted: Docker Hub does not support registry-wide catalog
12
12
  listing (it requires a registry-scoped token the Hub token service won't issue).
13
13
  """
@@ -15,6 +15,8 @@ listing (it requires a registry-scoped token the Hub token service won't issue).
15
15
  import json as _json
16
16
  from typing import Any
17
17
 
18
+ from scion_sdk.mcp.destructive import destructive_operation
19
+
18
20
  from dockerhub_api.api.api_client_registry_base import (
19
21
  ACCEPT_MANIFESTS,
20
22
  OCTET_STREAM,
@@ -96,6 +98,7 @@ class RegistryApi(RegistryApiBase):
96
98
  }
97
99
  return envelope
98
100
 
101
+ @destructive_operation
99
102
  def put_manifest(
100
103
  self,
101
104
  repo: str,
@@ -104,7 +107,6 @@ class RegistryApi(RegistryApiBase):
104
107
  media_type: str,
105
108
  ) -> dict[str, Any]:
106
109
  """``PUT /v2/{repo}/manifests/{reference}`` — push a manifest (gated)."""
107
- self._guard_destructive("put_manifest")
108
110
  body = manifest if isinstance(manifest, str) else _json.dumps(manifest)
109
111
  return self._registry_request(
110
112
  "PUT",
@@ -115,13 +117,13 @@ class RegistryApi(RegistryApiBase):
115
117
  content_type=media_type,
116
118
  )
117
119
 
120
+ @destructive_operation
118
121
  def delete_manifest(self, repo: str, reference: str) -> dict[str, Any]:
119
122
  """``DELETE /v2/{repo}/manifests/{digest}`` — delete a tag/manifest (gated).
120
123
 
121
124
  Docker Hub requires deletion by digest; pass a tag and it is resolved
122
125
  to its digest first.
123
126
  """
124
- self._guard_destructive("delete_manifest")
125
127
  target = reference
126
128
  if not reference.startswith("sha256:"):
127
129
  resolved = self.resolve_digest(repo, reference)
@@ -140,13 +142,13 @@ class RegistryApi(RegistryApiBase):
140
142
  """``HEAD /v2/{repo}/blobs/{digest}`` — blob existence check."""
141
143
  return self._registry_exists(repo, f"/blobs/{digest}")
142
144
 
145
+ @destructive_operation
143
146
  def delete_blob(self, repo: str, digest: str) -> dict[str, Any]:
144
147
  """``DELETE /v2/{repo}/blobs/{digest}`` — delete a blob (gated).
145
148
 
146
149
  Often returns ``405`` on Docker Hub (blob delete disabled); the status
147
150
  is surfaced cleanly rather than masked.
148
151
  """
149
- self._guard_destructive("delete_blob")
150
152
  return self._registry_request(
151
153
  "DELETE",
152
154
  repo,
@@ -293,13 +295,14 @@ class RegistryApi(RegistryApiBase):
293
295
 
294
296
  # ------------------------------- push -------------------------------- #
295
297
 
298
+ @destructive_operation
296
299
  def start_upload(self, repo: str) -> dict[str, Any]:
297
300
  """``POST /v2/{repo}/blobs/uploads/`` — open a blob upload session (gated)."""
298
- self._guard_destructive("start_upload")
299
301
  return self._registry_request(
300
302
  "POST", repo, "/blobs/uploads/", scope_actions="pull,push"
301
303
  )
302
304
 
305
+ @destructive_operation
303
306
  def upload_chunk(
304
307
  self,
305
308
  repo: str,
@@ -308,7 +311,6 @@ class RegistryApi(RegistryApiBase):
308
311
  content_range: str | None = None,
309
312
  ) -> dict[str, Any]:
310
313
  """``PATCH {location}`` — upload one blob chunk (gated)."""
311
- self._guard_destructive("upload_chunk")
312
314
  extra = {"Content-Range": content_range} if content_range else None
313
315
  return self._registry_request(
314
316
  "PATCH",
@@ -320,6 +322,7 @@ class RegistryApi(RegistryApiBase):
320
322
  extra_headers=extra,
321
323
  )
322
324
 
325
+ @destructive_operation
323
326
  def complete_upload(
324
327
  self,
325
328
  repo: str,
@@ -328,7 +331,6 @@ class RegistryApi(RegistryApiBase):
328
331
  final_chunk: bytes | None = None,
329
332
  ) -> dict[str, Any]:
330
333
  """``PUT {location}?digest=`` — finalize a blob upload (gated)."""
331
- self._guard_destructive("complete_upload")
332
334
  return self._registry_request(
333
335
  "PUT",
334
336
  repo,
@@ -339,9 +341,9 @@ class RegistryApi(RegistryApiBase):
339
341
  content_type=OCTET_STREAM,
340
342
  )
341
343
 
344
+ @destructive_operation
342
345
  def mount_blob(self, repo: str, digest: str, from_repo: str) -> dict[str, Any]:
343
346
  """``POST /v2/{repo}/blobs/uploads/?mount=&from=`` — cross-repo mount (gated)."""
344
- self._guard_destructive("mount_blob")
345
347
  return self._registry_request(
346
348
  "POST",
347
349
  repo,
@@ -74,7 +74,6 @@ class RegistryApiBase(DockerHubApiBase):
74
74
  registry_token_manager: Any | None = None,
75
75
  tls_profile: ResolvedTLSProfile | None = None,
76
76
  timeout: float | None = None,
77
- allow_destructive: bool = False,
78
77
  debug: bool = False,
79
78
  transport: httpx.BaseTransport | None = None,
80
79
  **kwargs: Any,
@@ -85,7 +84,6 @@ class RegistryApiBase(DockerHubApiBase):
85
84
  url=url or DEFAULT_REGISTRY_URL,
86
85
  tls_profile=tls_profile,
87
86
  timeout=timeout if timeout is not None else 30.0,
88
- allow_destructive=allow_destructive,
89
87
  debug=debug,
90
88
  transport=transport,
91
89
  **kwargs,
@@ -339,12 +339,6 @@ def _resolve_tls_profile(
339
339
  )
340
340
 
341
341
 
342
- def _resolve_allow_destructive(allow_destructive: bool | None) -> bool:
343
- if allow_destructive is None:
344
- return setting("DOCKERHUB_ALLOW_DESTRUCTIVE", False)
345
- return allow_destructive
346
-
347
-
348
342
  @dataclass
349
343
  class _HubCredentials:
350
344
  url: str
@@ -407,7 +401,6 @@ def get_client(
407
401
  token: str | None = None,
408
402
  jwt: str | None = None,
409
403
  tls_profile: ResolvedTLSProfile | None = None,
410
- allow_destructive: bool | None = None,
411
404
  config: dict | None = None,
412
405
  ) -> Any:
413
406
  """Factory for the Docker Hub :class:`~dockerhub_api.api_client.Api` client.
@@ -422,8 +415,8 @@ def get_client(
422
415
  password, a PAT ``dckr_pat_*``, or an org token).
423
416
  3. Anonymous — public, unauthenticated endpoints only.
424
417
 
425
- ``DOCKERHUB_ALLOW_DESTRUCTIVE`` (default ``False``) gates deletes and
426
- org-settings writes. CONCEPT:DH-OS.identity.destructive-action-gating-member — destructive-action gating.
418
+ Deletes and org-settings writes are declared destructive with the SDK and ask the caller to confirm.
419
+ CONCEPT:DH-OS.identity.destructive-action-gating-member — destructive-action gating.
427
420
 
428
421
  A resolved ``username`` (personal account or org) the caller's identity is
429
422
  not entitled to is denied before any credential exchange happens.
@@ -433,7 +426,6 @@ def get_client(
433
426
  creds = _resolve_hub_credentials(url, username, token, jwt, config)
434
427
  _deny_unless_entitled(creds.username)
435
428
  profile = _resolve_tls_profile(tls_profile)
436
- allow_destructive = _resolve_allow_destructive(allow_destructive)
437
429
 
438
430
  if creds.jwt:
439
431
  logger.info("Using pre-minted Docker Hub JWT")
@@ -441,7 +433,6 @@ def get_client(
441
433
  url=creds.url,
442
434
  token=creds.jwt,
443
435
  tls_profile=profile,
444
- allow_destructive=allow_destructive,
445
436
  )
446
437
 
447
438
  if creds.username and creds.token:
@@ -459,14 +450,13 @@ def get_client(
459
450
  url=creds.url,
460
451
  token_manager=manager,
461
452
  tls_profile=profile,
462
- allow_destructive=allow_destructive,
463
453
  )
464
454
 
465
455
  logger.warning(
466
456
  "No Docker Hub credentials configured — anonymous client "
467
457
  "(public endpoints only). Set DOCKERHUB_USERNAME and DOCKERHUB_TOKEN."
468
458
  )
469
- return Api(url=creds.url, tls_profile=profile, allow_destructive=allow_destructive)
459
+ return Api(url=creds.url, tls_profile=profile)
470
460
 
471
461
 
472
462
  def _resolve_credentials(
@@ -509,7 +499,6 @@ def get_registry_client(
509
499
  username: str | None = None,
510
500
  token: str | None = None,
511
501
  tls_profile: ResolvedTLSProfile | None = None,
512
- allow_destructive: bool | None = None,
513
502
  config: dict | None = None,
514
503
  transport: Any | None = None,
515
504
  ) -> Any:
@@ -519,7 +508,7 @@ def get_registry_client(
519
508
  scoped tokens via :class:`RegistryTokenManager` using the same
520
509
  ``DOCKER_HUB_USER`` / ``DOCKER_HUB_TOKEN`` credentials (anonymous when
521
510
  unset, which still works for public pulls). Pushes, deletes, and blob
522
- uploads are gated by ``DOCKERHUB_ALLOW_DESTRUCTIVE``.
511
+ uploads are declared destructive with the SDK.
523
512
 
524
513
  A resolved ``username`` (personal account or org) the caller's identity is
525
514
  not entitled to is denied before any token is minted.
@@ -541,7 +530,6 @@ def get_registry_client(
541
530
  username, token = _resolve_credentials(username, token, config)
542
531
  _deny_unless_entitled(username)
543
532
  profile = _resolve_tls_profile(tls_profile)
544
- allow_destructive = _resolve_allow_destructive(allow_destructive)
545
533
 
546
534
  token_manager = RegistryTokenManager(
547
535
  username=username,
@@ -555,7 +543,6 @@ def get_registry_client(
555
543
  url=url,
556
544
  registry_token_manager=token_manager,
557
545
  tls_profile=profile,
558
- allow_destructive=allow_destructive,
559
546
  transport=transport,
560
547
  )
561
548