@topolo/cli 0.13.13 → 0.13.16
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.
- package/README.md +187 -8
- package/agent-packs/templates/career/skills/topolo/SKILL.md +7 -0
- package/agent-packs/templates/clients/skills/topolo/SKILL.md +7 -0
- package/agent-packs/templates/creative/skills/topolo/SKILL.md +7 -0
- package/agent-packs/templates/founder/skills/topolo/SKILL.md +7 -0
- package/agent-packs/templates/learning/skills/topolo/SKILL.md +7 -0
- package/agent-packs/templates/personal/skills/topolo/SKILL.md +7 -0
- package/agent-packs/templates/research/skills/topolo/SKILL.md +7 -0
- package/agent-packs/templates/ventures/skills/topolo/SKILL.md +7 -0
- package/dist/index.js +1586 -175
- package/package.json +3 -3
- package/skills/topolo/SKILL.md +19 -2
package/README.md
CHANGED
|
@@ -74,27 +74,50 @@ OAuth errors, and never extends the original device-code expiry.
|
|
|
74
74
|
### Contexts
|
|
75
75
|
|
|
76
76
|
A user can act in their personal space or any organization they belong to. Each
|
|
77
|
-
context has its own credential
|
|
77
|
+
context has its own credential. Name the one to act as with `--context <key>` or
|
|
78
|
+
`TOPOLO_CONTEXT`; without a pin the CLI (and the MCP) use the stored context only
|
|
79
|
+
while it is the only one signed in for the environment.
|
|
78
80
|
These credentials are independent from the shared web preference: switching a
|
|
79
81
|
CLI/MCP context never moves the browser session, and a web switch never changes
|
|
80
82
|
an already-issued CLI/MCP token or its refresh chain.
|
|
81
83
|
|
|
82
84
|
```bash
|
|
83
|
-
topolo context ls # personal + your orgs, and which is
|
|
84
|
-
topolo context use <organization|personal> #
|
|
85
|
+
topolo context ls # personal + your orgs, and which is the stored default
|
|
86
|
+
topolo context use <organization|personal> # set the stored default (validated + a fresh token minted server-side)
|
|
87
|
+
topolo --context org:org_acme whoami # pin one command to one context
|
|
85
88
|
```
|
|
86
89
|
|
|
90
|
+
**No implicit default context.** Once several contexts are signed in for an
|
|
91
|
+
environment, every account command without `--context`/`TOPOLO_CONTEXT` refuses
|
|
92
|
+
with "the context is no longer safe to assume", exactly like the
|
|
93
|
+
multi-environment `--env` guard. `topolo context use` stays available for anyone
|
|
94
|
+
who wants a stored default, but nothing relies on it implicitly.
|
|
95
|
+
|
|
96
|
+
`topolo auth login` stores the approved sign-in under its own context key and
|
|
97
|
+
never moves the stored default (`--use` opts in; the environment's first sign-in
|
|
98
|
+
becomes the default). It prints which user and organization were approved. To
|
|
99
|
+
re-authorise one context, name it:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
topolo --env production --context org:org_acme auth login
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If the browser approves a different user or organization, the CLI warns that
|
|
106
|
+
`org:org_acme` was not renewed and exits 1.
|
|
107
|
+
|
|
87
108
|
### Wire into agent hosts
|
|
88
109
|
|
|
89
110
|
```bash
|
|
90
|
-
topolo setup #
|
|
111
|
+
topolo setup # registers the exact bundled MCP, pinned to the only signed-in context
|
|
91
112
|
topolo setup --all # configure every supported local host
|
|
92
113
|
topolo doctor # verifies auth, handshake, tool discovery, and identity
|
|
93
114
|
```
|
|
94
115
|
|
|
95
116
|
The MCP reads the same credential `topolo auth login` writes — no API key to paste.
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
Every registration setup writes pins `TOPOLO_ENV` and `TOPOLO_CONTEXT`. Without
|
|
118
|
+
`--context`, setup pins the only signed-in context, and refuses when there are
|
|
119
|
+
none or several. For each additional organization, use an exact context key
|
|
120
|
+
returned by `topolo context ls --json`:
|
|
98
121
|
|
|
99
122
|
```bash
|
|
100
123
|
topolo setup --name topolo-acme --context org:org_acme --env production
|
|
@@ -114,11 +137,96 @@ The local installer does not edit JetBrains AI Assistant settings or web-only
|
|
|
114
137
|
connectors because those are managed through their own UI or remote connector
|
|
115
138
|
configuration. Use the generated native MCP entry as the import source there.
|
|
116
139
|
|
|
140
|
+
### Shared local MCP hub (opt-in)
|
|
141
|
+
|
|
142
|
+
Every stdio registration starts its own Node process in every agent thread, so
|
|
143
|
+
N threads x M pinned identities means N x M MCP processes. The shared hub runs
|
|
144
|
+
**one** loopback process that serves every identity at
|
|
145
|
+
`http://127.0.0.1:<port>/mcp/<name>`; Claude Code and Codex connect to it by URL.
|
|
146
|
+
Plain `topolo setup` keeps the stdio path; `--shared` is opt-in.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
topolo --env production setup --shared --name topolo-acme --context org:org_acme
|
|
150
|
+
topolo --env staging setup --shared --name topolo-acme-stg --context org:org_acme
|
|
151
|
+
topolo setup --shared --import-stdio # move every existing stdio topolo/topolo-* entry onto the hub
|
|
152
|
+
topolo doctor --shared # hub, token, unit, registrations, then MCP whoami per identity
|
|
153
|
+
topolo mcp serve [--port 7337] # what the keep-alive unit runs; foreground for debugging
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`setup --shared`:
|
|
157
|
+
|
|
158
|
+
1. Adds the identity (`{name, env, context}`, exactly what a stdio entry pins
|
|
159
|
+
with `TOPOLO_ENV` + `TOPOLO_CONTEXT`; both are always pinned, and without
|
|
160
|
+
`--context` the only signed-in context is pinned) to `mcp-hub.json` next to
|
|
161
|
+
the credential store (`~/.config/topolo/mcp-hub.json`, or the directory of
|
|
162
|
+
`TOPOLO_CONFIG_PATH`). `--import-stdio` converts existing stdio entries and
|
|
163
|
+
skips ones the hub cannot serve (entries with no `TOPOLO_CONTEXT` pin,
|
|
164
|
+
private `TOPOLO_CONFIG_PATH` profiles, agent installations, invalid or
|
|
165
|
+
conflicting definitions). Add a skipped unpinned entry back with
|
|
166
|
+
`--name <name> --context <context>`.
|
|
167
|
+
2. Creates `mcp-hub.token` (256-bit, mode 0600) once and reuses it.
|
|
168
|
+
3. Registers **every** hub identity with Claude Code and Codex at user scope,
|
|
169
|
+
replacing same-name stdio entries. `--project` is refused so the token can
|
|
170
|
+
never land in a repository file. Other hosts stay on stdio.
|
|
171
|
+
4. Installs a keep-alive unit running the exact Node binary and CLI entry that
|
|
172
|
+
ran setup (`--no-service` to skip): `~/Library/LaunchAgents/io.topolo.mcp-hub.plist`
|
|
173
|
+
(launchd, `KeepAlive`, logs to `~/Library/Logs/topolo-mcp-hub.log`) or
|
|
174
|
+
`~/.config/systemd/user/topolo-mcp-hub.service` (`Restart=always`). Only
|
|
175
|
+
`TOPOLO_CONFIG_PATH`/`XDG_CONFIG_HOME` are passed through; PATH and secrets
|
|
176
|
+
are not. With `--agent-home` the unit is written there but not loaded.
|
|
177
|
+
|
|
178
|
+
Exact entries written (token shown as `<token>`):
|
|
179
|
+
|
|
180
|
+
```jsonc
|
|
181
|
+
// ~/.claude.json (written 0600)
|
|
182
|
+
"mcpServers": {
|
|
183
|
+
"topolo-acme": {
|
|
184
|
+
"type": "http",
|
|
185
|
+
"url": "http://127.0.0.1:7337/mcp/topolo-acme",
|
|
186
|
+
"headers": { "Authorization": "Bearer <token>", "X-Topolo-Agent": "claude-code" }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```toml
|
|
192
|
+
# ~/.codex/config.toml (written 0600)
|
|
193
|
+
[mcp_servers.topolo-acme]
|
|
194
|
+
url = "http://127.0.0.1:7337/mcp/topolo-acme"
|
|
195
|
+
http_headers = { "Authorization" = "Bearer <token>", "X-Topolo-Agent" = "codex" }
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Codex streamable-HTTP servers accept `url`, `bearer_token_env_var`,
|
|
199
|
+
`http_headers` and `env_http_headers`; an inline `bearer_token` is rejected.
|
|
200
|
+
`http_headers` is used so the hub works when Codex is launched from a GUI with
|
|
201
|
+
no shell environment. To keep the token out of `config.toml`, replace the
|
|
202
|
+
`Authorization` header with `bearer_token_env_var = "TOPOLO_MCP_HUB_TOKEN"` and
|
|
203
|
+
export that variable (contents of `mcp-hub.token`) into Codex's environment.
|
|
204
|
+
|
|
205
|
+
Hub behaviour and security:
|
|
206
|
+
|
|
207
|
+
- Binds `127.0.0.1` only; there is no host option.
|
|
208
|
+
- Refuses any request whose `Host` is not `127.0.0.1`, `localhost` or `[::1]`
|
|
209
|
+
on the hub port, or that carries a non-loopback `Origin` (DNS rebinding and
|
|
210
|
+
browser cross-origin calls), before checking the bearer token
|
|
211
|
+
(constant-time). Only then is `/mcp/<name>` routed; `GET /health` lists
|
|
212
|
+
identity names.
|
|
213
|
+
- Each identity has its own handler (`@topolo/mcp/local`) that resolves and
|
|
214
|
+
refreshes its credential from the local store exactly as a pinned stdio MCP
|
|
215
|
+
does. Process-wide `TOPOLO_API_KEY`/`TOPOLO_ACCESS_TOKEN`/`TOPOLO_CONTEXT`,
|
|
216
|
+
request headers and other identities are never consulted; the hub token is
|
|
217
|
+
stripped before the request reaches an identity.
|
|
218
|
+
- Stateless Streamable HTTP (no session ids), so restarts never strand a client.
|
|
219
|
+
- `mcp-hub.json` is re-read when it changes; adding an identity needs no restart.
|
|
220
|
+
- Logs carry method, path, status and duration only.
|
|
221
|
+
- Rotating the token: delete `mcp-hub.token`, rerun `topolo setup --shared`,
|
|
222
|
+
then restart the agents.
|
|
223
|
+
|
|
117
224
|
## Cross-org isolation
|
|
118
225
|
|
|
119
226
|
The CLI **never accepts an `orgId` argument**. Every command is scoped to the
|
|
120
|
-
|
|
121
|
-
in a different organization you
|
|
227
|
+
context embedded in the credential (personal or a specific org); to act
|
|
228
|
+
in a different organization you pin that context's own stored credential
|
|
229
|
+
(`--context`), or create one with `topolo context use`, which
|
|
122
230
|
mints a new context-scoped credential — you can never target another org by
|
|
123
231
|
passing an id. The context remains bound through refresh rotation and is not
|
|
124
232
|
re-hydrated from the user's current web selection. This is enforced in two places:
|
|
@@ -224,6 +332,77 @@ Both resource flags are required together. This supports concurrent agent
|
|
|
224
332
|
operations across many app-local workspaces without changing the CLI/MCP org
|
|
225
333
|
context or the user's web switcher.
|
|
226
334
|
|
|
335
|
+
### `topolo credentials write-custody`
|
|
336
|
+
|
|
337
|
+
Local CLI and stdio MCP replace credentials in action results with opaque
|
|
338
|
+
`topolo-secret:<uuid>` references held in the OS protected store (macOS
|
|
339
|
+
Keychain, Linux Secret Service, Windows DPAPI). Both use the same store service
|
|
340
|
+
and the same binding (environment, verified principal, organization and API key
|
|
341
|
+
identity), so a reference returned by a stdio MCP call can be used by the CLI
|
|
342
|
+
pinned to the same identity and environment. References expire after 30 minutes.
|
|
343
|
+
|
|
344
|
+
When a service such as the release controller needs a scoped API key in a
|
|
345
|
+
custody file, write it straight from the reference so no person or agent sees
|
|
346
|
+
the plaintext:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
export TOPOLO_CONTEXT=org:<organizationId>
|
|
350
|
+
created=$(topolo --env production actions call app_topolo_auth.api_keys.create \
|
|
351
|
+
--data '{"appId":"<appId>","name":"<label>","scopes":["<scope>"],"expiresInDays":90}' \
|
|
352
|
+
--confirm --detail --json) # data.key.secret is a topolo-secret reference
|
|
353
|
+
topolo --env production credentials write-custody "$(jq -r .data.key.secret <<<"$created")" \
|
|
354
|
+
--api-key-id "$(jq -r .data.key.id <<<"$created")" --app <appId> \
|
|
355
|
+
--out /absolute/custody/dir/production --confirm --json
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The command:
|
|
359
|
+
|
|
360
|
+
1. Requires `--confirm`, an absolute `--out`, `--app` and `--api-key-id`.
|
|
361
|
+
Keep one key per environment in its own subdirectory (for example
|
|
362
|
+
`.../catalog-publication-credentials/<environment>/`). When the last path
|
|
363
|
+
component is an environment name it must equal the credential's environment
|
|
364
|
+
(`custody_environment_mismatch` otherwise), and `custody.json.environment`
|
|
365
|
+
is always the environment the credential, protected store and Auth calls
|
|
366
|
+
are bound to.
|
|
367
|
+
2. Creates `--out` (and any missing parents) with mode 0700, or accepts an existing 0700 directory owned
|
|
368
|
+
by the current user. Every path component must be a real directory (no
|
|
369
|
+
symbolic links); parents must be owned by root or the current user and must
|
|
370
|
+
not be group- or world-writable (root-owned sticky directories such as
|
|
371
|
+
`/tmp` are allowed).
|
|
372
|
+
3. Refuses to overwrite `api-key.json` or `custody.json` without `--replace`,
|
|
373
|
+
and refuses symbolic-link or non-regular destinations even with it.
|
|
374
|
+
4. Reads the key's metadata from Auth `app_topolo_auth.api_keys.list` with the
|
|
375
|
+
pinned credential and requires the same organization, the `--app`
|
|
376
|
+
application, status `active` and an unexpired key.
|
|
377
|
+
5. Resolves the reference through the SDK protected store with its binding
|
|
378
|
+
checks, then verifies the key against Auth introspection: it must be that
|
|
379
|
+
API key id, application, organization and scope set.
|
|
380
|
+
6. Writes, each 0600 through a temporary file and rename in the same directory:
|
|
381
|
+
- `api-key.json`: `{"schemaVersion":1,"apiKey":"<secret>"}`
|
|
382
|
+
- `custody.json`: `{schemaVersion, appId, scopes, organizationId, environment,
|
|
383
|
+
apiKeyId, expiresAt, keyDigest, createdAt}`. `expiresAt` and `createdAt`
|
|
384
|
+
are Auth's values unchanged (epoch seconds, `expiresAt` null for no
|
|
385
|
+
expiry); `keyDigest` is `sha256:<hex>` of the secret.
|
|
386
|
+
7. Deletes the local protected reference, like `actions release-secret`. This
|
|
387
|
+
does **not** revoke the upstream key; use `app_topolo_auth.api_keys.revoke`.
|
|
388
|
+
|
|
389
|
+
Output (JSON or text) carries only paths, metadata and the digest. Refusals use
|
|
390
|
+
stable `error.code` values: `custody_confirmation_required`,
|
|
391
|
+
`custody_invalid_input`, `custody_path_not_absolute`, `custody_environment_mismatch`, `custody_platform_unsupported`,
|
|
392
|
+
`custody_path_symlink`, `custody_path_not_directory`, `custody_path_not_regular`,
|
|
393
|
+
`custody_parent_insecure`, `custody_dir_insecure`, `custody_file_exists`,
|
|
394
|
+
`custody_organization_required`, `custody_metadata_unavailable`,
|
|
395
|
+
`custody_key_not_found`, `custody_app_mismatch`, `custody_organization_mismatch`,
|
|
396
|
+
`custody_key_inactive`, `custody_key_expired`, `custody_secret_unsupported_type`,
|
|
397
|
+
`custody_key_verification_failed`, `custody_key_mismatch`,
|
|
398
|
+
`custody_metadata_mismatch`, `custody_reference_release_failed` and
|
|
399
|
+
`custody_error_withheld`, plus the protected store's `invalid_action_secret_reference`,
|
|
400
|
+
`action_secret_unavailable` (unknown, or bound to another principal,
|
|
401
|
+
organization, API key or environment), `action_secret_expired`,
|
|
402
|
+
`action_secret_invalid` and `action_secret_identity_required`. A refusal before
|
|
403
|
+
step 6 writes no custody file and keeps the reference (step 2 may already have
|
|
404
|
+
created the empty 0700 directory).
|
|
405
|
+
|
|
227
406
|
### `topolo workspaces`
|
|
228
407
|
|
|
229
408
|
Discovers canonical workspaces and the caller-specific creation policy for a
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|
|
@@ -270,6 +270,13 @@ Local deletion does not revoke the upstream credential; use its published
|
|
|
270
270
|
revocation action separately when required. Other SDK/remote hosts keep
|
|
271
271
|
redaction unless they explicitly provide a protected store.
|
|
272
272
|
|
|
273
|
+
The only supported way to put a referenced API key on disk is an
|
|
274
|
+
operator-approved custody file:
|
|
275
|
+
`topolo credentials write-custody <reference> --out <absolute-dir> --app <appId> --api-key-id <id> --confirm --json`
|
|
276
|
+
verifies the key against Auth, writes 0600 `api-key.json` and `custody.json`
|
|
277
|
+
(metadata and a SHA-256 digest only) into a 0700 directory, prints no secret,
|
|
278
|
+
and deletes the local reference without revoking the upstream key.
|
|
279
|
+
|
|
273
280
|
## Generic public API escape hatch
|
|
274
281
|
|
|
275
282
|
Only when public docs provide a complete contract:
|