@tinycloud/cli 1.2.0-beta.0 → 1.2.0-beta.2
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 +10 -1
- package/dist/index.js +3079 -2954
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +13193 -12216
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +4 -4
- package/skills/tc-cli/AUTH.md +22 -2
- package/skills/tc-cli/REFERENCE.md +54 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinycloud/cli",
|
|
3
|
-
"version": "1.2.0-beta.
|
|
3
|
+
"version": "1.2.0-beta.2",
|
|
4
4
|
"description": "TinyCloud CLI - manage data, delegations, and nodes from the terminal",
|
|
5
5
|
"author": "TinyCloud, Inc.",
|
|
6
6
|
"license": "EGPL",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@tinycloud/share-sdk": "1.1.0",
|
|
33
33
|
"@tinycloud/share-envelope": "1.1.0",
|
|
34
|
-
"@tinycloud/node-sdk": "3.2.0-beta.
|
|
34
|
+
"@tinycloud/node-sdk": "3.2.0-beta.2",
|
|
35
35
|
"@tinycloud/node-sdk-wasm": "1.7.7",
|
|
36
|
-
"@tinycloud/sdk-core": "3.2.0-beta.
|
|
37
|
-
"@tinycloud/operations": "0.5.0-beta.
|
|
36
|
+
"@tinycloud/sdk-core": "3.2.0-beta.2",
|
|
37
|
+
"@tinycloud/operations": "0.5.0-beta.2",
|
|
38
38
|
"chalk": "^5.3.0",
|
|
39
39
|
"commander": "^14.0.0",
|
|
40
40
|
"open": "^11.0.0",
|
package/skills/tc-cli/AUTH.md
CHANGED
|
@@ -90,9 +90,28 @@ tc --profile agent-secrets secrets get OPENAI_API_KEY --raw
|
|
|
90
90
|
- If the owner unchecks decrypt, the login still succeeds and lists the decrypt entry in `declined`; reads of that secret then fail. Older OpenKey deployments sign decrypt inside the owner's secrets space rather than as a top-level network grant. That does not grant raw decrypt: it is listed in `declined` with an old-deployment warning and omitted from saved approved permissions. A later `auth request --grant` or secret-read escalation refuses that old nested proof before activating or storing a delegation, with `OPENKEY_GRANT_BROADENED`; obtain a new approval from a deployment that signs the raw network grant.
|
|
91
91
|
- A secrets command (`get`, `list`, `put`, `delete`) on an OpenKey profile without a grant for the name fails fast only when neither stdin nor stderr is a terminal: `PERMISSION_DENIED` (exit 5) with a hint naming this scoped login. Redirecting stdout (`|`, `>`, or command substitution) does not prevent a person at a terminal from seeing the browser approval prompt. If the session is missing or expired, a headless secrets command, including the canonical `secrets get` operation, instead fails with `AUTH_REQUIRED` (exit 3) and the scoped paste-login hint before invoking the node or starting an unscoped browser refresh.
|
|
92
92
|
- At a terminal, the owner approves the missing grant in OpenKey. The request carries the missing entries, `tinycloud.capabilities/read` on the space root (the deployed OpenKey refuses a request without it; newer OpenKey releases require it only when it is requested) and raw decrypt in the `encryption` pseudo-space with the owner's address EIP-55 checksummed; a decrypt-only request is placed in the `secrets` space (or `--space`), since OpenKey needs exactly one space. The signed `capabilities/read` counts as requested; anything else beyond the request is refused (`OPENKEY_GRANT_BROADENED`). The grant is stored with its signed SIWE proof (`siweProof`) and a request binding to exactly its signed capabilities (`cli-grant:<cid>`; see below). `secrets get` and other operations (including MCP), in this process or a new one, re-verify that proof against the profile's session key and owner, and hold the grant to its binding, before using it. A stored grant that fails that check (for example one approved by an earlier release, which stored no proof) is not used by them, and a read that needed it asks for approval again. Each such grant is reported by a fixed reason code (`proof_missing`, `unbound`, `outside_request`, `host_mismatch`, `activation_rejected`, ...) and, when the stored CID is the CID of the stored authorization bytes, that CID; never by error text. In JSON mode (`--json`, or stdout not a terminal) the warnings go inside the error's `warnings` array (beside any validated `meta`) when the command fails, or as one `{"warnings": [...]}` line on stderr after the command's output when it succeeds, and stderr carries nothing else; otherwise they are one stderr line saying to approve access again. Expired grants are dropped without a warning. A stale grant needs no cleanup: it expires on its own.
|
|
93
|
-
- A stored session that has expired, or no longer verifies for the profile's key, fails every command that needs it with `AUTH_REQUIRED` (exit 3) before any node request, whatever the profile kind; secrets commands on an owner OpenKey profile whose session has passed its expiry refresh it in the browser at a terminal instead. The hint names a new scoped paste login for a delegate profile, `auth login --method local` for a local owner key, and `auth login --method openkey` for an OpenKey owner; Share commands keep their `auth login --device --manifest builtin:share-publishing` guidance. It is never reported as a retryable node error. Operations and MCP report it as `AUTH_REQUIRED` (`retryable: false`), the code they also use for a node's 401.
|
|
93
|
+
- A stored session that has expired, or no longer verifies for the profile's key, fails every command that needs it with `AUTH_REQUIRED` (exit 3) before any node request, whatever the profile kind; secrets commands on an owner OpenKey profile whose session has passed its expiry refresh it in the browser at a terminal instead. A delegate session created by `tc auth import` has no SIWE; its lifetime is the `exp` its compact-UCAN delegation signs, read from the stored authorization bytes, so it is refused the same way, including sessions imported by earlier releases. Expiry is judged by this machine's clock. `context` reports that expiry, and an expired imported session does not block the scoped paste login the hint names. A session imported from a CACAO (wallet-signed) delegation is still refused only by the node. The hint names a new scoped paste login for a delegate profile, `auth login --method local` for a local owner key, and `auth login --method openkey` for an OpenKey owner; Share commands keep their `auth login --device --manifest builtin:share-publishing` guidance. It is never reported as a retryable node error. Operations and MCP report it as `AUTH_REQUIRED` (`retryable: false`), the code they also use for a node's 401.
|
|
94
94
|
- `secrets get -o FILE` validates the destination and its existing directory before fetching or decrypting secret bytes. It refuses symlinks, directories and devices, syncs a new owner-only (0600) file, then atomically replaces the destination. Directory sync is attempted after replacement where supported; its failure does not report a failed write when the destination was already replaced. A destination that cannot be replaced fails with a sanitized filesystem error naming the destination; there is no unsafe fallback.
|
|
95
95
|
|
|
96
|
+
### Asking the owner to add a missing secret
|
|
97
|
+
|
|
98
|
+
When the secret does not exist yet, the agent sends the owner one message with a request link, redeems the consent short code if the owner approved a scoped login, then waits. Call the CLI by absolute path: a bare `tc` on `PATH` can be iproute2's `/usr/sbin/tc` (traffic control), not TinyCloud.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
TC_BIN="$(npm prefix --global)/bin/tc"
|
|
102
|
+
"$TC_BIN" --profile agent-secrets secrets request OPENAI_API_KEY --requester "my-agent@$(hostname -s)" --json # offline; prints {url, names, owner}
|
|
103
|
+
# One message to the owner: the url (plus the scoped-login approval URL above if the profile has no grant yet).
|
|
104
|
+
# If the owner sends back a consent short code, redeem it first:
|
|
105
|
+
printf '%s\n' "$CODE" | "${LOGIN[@]}"
|
|
106
|
+
"$TC_BIN" --profile agent-secrets secrets wait OPENAI_API_KEY --timeout 9m --json # 0 ready, 8 still pending
|
|
107
|
+
"$TC_BIN" --profile agent-secrets secrets get OPENAI_API_KEY --raw
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- `wait` never signs in: it needs a stored session and doesn't accept `TC_PRIVATE_KEY` (exit 3). Exit 3 carries `error.recovery.command`, the persistent login to run once.
|
|
111
|
+
- After a CLI upgrade, run one normal command per profile first (for example `"$TC_BIN" --profile agent-secrets auth caps`). Otherwise the first `wait` returns `PROFILE_MIGRATION_REQUIRED` (exit 1) once, with that command in `error.recovery.command`.
|
|
112
|
+
- Exit 8 means the owner has not added it yet: rerun `wait` without sending the link again. Exit 5 means the grant does not cover a name: `error.recovery.names` lists them. [REFERENCE.md](REFERENCE.md#asking-the-owner-for-a-secret-request-wait-manage) has the full exit table.
|
|
113
|
+
- A local identity (local-owner profile, a profile with a stored `privateKey`, `--private-key`, `TC_PRIVATE_KEY`) owns its secrets and gets `LOCAL_OWNER_PROFILE` from `request`: store them with `secrets put NAME --stdin`.
|
|
114
|
+
|
|
96
115
|
## More spaces after login
|
|
97
116
|
|
|
98
117
|
Login covers one space. Request further spaces on the logged-in profile:
|
|
@@ -110,6 +129,7 @@ Delegations stored in a profile's `additional-delegations.json` replay only into
|
|
|
110
129
|
- **Request-bound import.** `tc auth import` of an artifact with a `requestId` (from `tc auth grant`, or MCP `tinycloud_auth_import`) binds the delegation to the stored request it was checked against. Importing a stored delegation again this way replaces its record and binding.
|
|
111
130
|
- **Import without a request.** `tc auth import` of a bare portable delegation, a stored record, or an envelope with no `requestId`, addressed to this profile's session key, verifies it and binds it to exactly its own signed capabilities: request ID `unbound-import:<cid>`, with an `authorityRequestAudit` note. A signed-login grant must carry its SIWE message and signature; an unproved session-addressed grant fails with `INVALID_AUTH_IMPORT`, and one with an invalid proof fails with `DELEGATION_REJECTED` without being saved. No request limits a valid import: the profile gets the delegation's full signed authority, whatever the artifact's `permissions` say. To limit authority, import against a request instead (`tc auth request --emit`, then `tc auth grant` by the owner, then `tc auth import`). This route never replaces or weakens an existing binding for the same CID.
|
|
112
131
|
- **Proofless cross-user imports.** A portable grant with no signed proof is refused with `INVALID_AUTH_IMPORT` even when addressed to another key. It is not written to the profile or grant history and cannot produce repeated replay warnings.
|
|
132
|
+
- **Expired compact delegations.** A compact-UCAN whose signed expiry has passed, or passes while the import is being stored, fails with `DELEGATION_EXPIRED` (exit 1) and leaves no delegation row or grant-history entry. A provisional first session is rolled back too; if another login or logout changed profile state meanwhile, its newer state is kept.
|
|
113
133
|
- **One-time migration.** Records stored by earlier releases have no binding. The first command that restores the profile's own session binds each verifiable record to exactly its own signed capabilities (`migrated:<cid>`, audit note `source: "migration"`) and writes `delegation-binding-migration.json`, listing bound and unbound CIDs. Records without proof cannot be migrated. Migration is best-effort and restartable under the profile lock: an interrupted run or a busy lock defers remaining records to the next command. The runtime never installs an unbound record during that wait or afterward; it reports `unbound` (or `proof_missing` for records without proof).
|
|
114
134
|
- Older releases ignore the new fields. They do not enforce bindings, and an older `tc auth import` of a CID that is already stored replaces its record with an unbound one, which this release then refuses until it is imported again.
|
|
115
135
|
- **CLI grants.** Signed-login grants from `tc auth request --grant` (OpenKey or local key) and secret-read approvals are checked against the request they answer, then stored with their `siweProof` and bound to their signed capabilities (`cli-grant:<cid>`). A local-key grant of only raw decrypt on the owner's network names no space; it is verified like any other grant. A new grant never replaces an existing bound record for the same CID. Earlier CLI releases stored signed-login grants without a proof: these are now refused with `proof_missing` on every replay path, even if a binding was later added. A grant with proof but without a valid binding reports `unbound`; cross-user delegations addressed to the profile's identity rather than its session key remain separate and are not installed by replay.
|
|
@@ -132,7 +152,7 @@ To replace an old grant without a proof, an owner can approve again with `tc aut
|
|
|
132
152
|
|
|
133
153
|
`tc init` and `tc auth login` without a manifest request broad default-space consent, including writes, and replace the profile's session as before. Prefer `init --key-only` followed by a scoped login. They still verify OpenKey's signed proof whenever it carries one, the profile records an owner, or `--expiry` is given, and they keep the recorded owner. Non-interactive `auth login` never switches to device mode on its own, and never waits on a browser that cannot open: without `--device --manifest`, `--paste` or `--no-popup` it fails fast with `INTERACTIVE_LOGIN_REQUIRED`.
|
|
134
154
|
|
|
135
|
-
Only an explicit `--host` becomes the profile's stored host; `TC_HOST` and a discovered local node apply to that command only.
|
|
155
|
+
Only an explicit `--host` becomes the profile's stored host; `TC_HOST` and a discovered local node apply to that command only. A command that uses the stored session verifies it locally before probing a configured local node (`localNodeUrl`, `localLinkName`); an expired or invalid one is refused without contacting it. Login, logout, rotate, status, whoami, context, node, profile, `space host-request|switch`, `enable` and `auth request` without `--grant` probe as before, as does any command given `--private-key` or `TC_PRIVATE_KEY`, or a profile with no stored session.
|
|
136
156
|
|
|
137
157
|
`tc auth logout` clears the local session and keeps the key. It is not node revocation, app disconnection, or removal of an agent's conversation history.
|
|
138
158
|
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
<!-- BEGIN GENERATED TINYCloud operations coverage -->
|
|
4
4
|
This release has **Commander coverage tracked, not complete parity**:
|
|
5
5
|
|
|
6
|
-
-
|
|
6
|
+
- 2 migrated registration(s).
|
|
7
7
|
- 1 partially migrated registration(s).
|
|
8
|
-
-
|
|
8
|
+
- 127 legacy registration(s) remain Commander-owned.
|
|
9
9
|
|
|
10
10
|
- `auth import [source]` → `tinycloud.auth.import@1` (partial; legacy inputs: v1 delegation artifact, v1 permission artifact without command, bare portable delegation, stored delegation wrapper, cross-user delegation persisted with activated=false).
|
|
11
11
|
- `secrets get <name>` → `tinycloud.secrets.get@1` (migrated).
|
|
12
|
+
- `secrets probe <name>` → `tinycloud.secrets.status@1` (migrated).
|
|
12
13
|
<!-- END GENERATED TINYCloud operations coverage -->
|
|
13
14
|
|
|
14
15
|
## Calling the CLI
|
|
@@ -138,13 +139,60 @@ tc secrets network init
|
|
|
138
139
|
tc secrets network show
|
|
139
140
|
tc secrets network grant did:pkh:eip155:1:0xRecipient...
|
|
140
141
|
|
|
141
|
-
tc secrets put ANTHROPIC_API_KEY
|
|
142
|
+
printf '%s' "$ANTHROPIC_API_KEY" | tc secrets put ANTHROPIC_API_KEY --stdin # keeps the value out of argv and shell history
|
|
142
143
|
tc secrets get ANTHROPIC_API_KEY
|
|
143
144
|
tc secrets get ANTHROPIC_API_KEY -o key.txt # atomic owner-only (0600) replacement
|
|
144
145
|
tc secrets list
|
|
145
146
|
tc secrets delete ANTHROPIC_API_KEY
|
|
146
147
|
```
|
|
147
148
|
|
|
149
|
+
A missing secret makes `tc secrets get` exit 4 (`NOT_FOUND`). For an identity that is not local, `error.setup` carries the owner's request link; a local identity (or a name/scope no link can express) gets only a hint naming `tc secrets put NAME --stdin`. `tc secrets doctor NAME` hints `tc secrets request NAME` for a missing secret, or `tc secrets put NAME --stdin` for a local identity. `tc secrets get --delegation` follows the same rule, and a delegated read the node refuses (403, a capability 401, `AUTH_UNAUTHORIZED`) exits 5.
|
|
150
|
+
|
|
151
|
+
### Asking the owner for a secret: `request`, `wait`, `manage`
|
|
152
|
+
|
|
153
|
+
`/usr/sbin/tc` (iproute2 traffic control) can shadow TinyCloud's `tc` on `PATH`, so scripts call the CLI by absolute path:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
TC_BIN="$(npm prefix --global)/bin/tc"
|
|
157
|
+
"$TC_BIN" --profile agent secrets request OPENAI_API_KEY --requester "agent@host" --json
|
|
158
|
+
# {"url":"https://secrets.tinycloud.xyz/request?owner=…&name=OPENAI_API_KEY&requester=…&v=1","names":["OPENAI_API_KEY"],"owner":"did:pkh:eip155:1:0x…"}
|
|
159
|
+
# send the url to the owner once, then:
|
|
160
|
+
"$TC_BIN" --profile agent secrets wait OPENAI_API_KEY --timeout 9m --json
|
|
161
|
+
"$TC_BIN" --profile agent secrets get OPENAI_API_KEY --raw
|
|
162
|
+
"$TC_BIN" secrets manage OPENAI_API_KEY --no-open --json # {"url":"…","opened":false}: the replace/manage page
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
- **`secrets request <NAME...> [--scope S | --app ID] [--owner DID] [--requester LABEL] [--open]`** prints `{url, names, scope?, owner}` and exits 0. It is offline: no session, no node request. 1–10 names (`^[A-Z][A-Z0-9_]*$`, ≤ 64, duplicates dropped). `--app ID` derives the scope from an app id (`xyz.tinycloud.feed` → `xyz-tinycloud-feed`); `--app` with `--scope` exits 2. `--requester` defaults to `user@host`. `--open` also opens the link; a failure to open only adds a `BROWSER_OPEN_FAILED` warning on stderr. Errors: 2 for `INVALID_SECRET_NAME`, `TOO_MANY_SECRET_NAMES`, `INVALID_SECRET_SCOPE`, `INVALID_REQUESTER`, `INVALID_OWNER`, `OWNER_MISMATCH`, `LOCAL_OWNER_PROFILE`; 3 for `OWNER_DID_UNKNOWN` (pass `--owner`).
|
|
166
|
+
- **`secrets wait <NAME...> [--scope S | --app ID] [--owner DID] [--timeout D]`** polls until every name is readable. `D` is `0`, `Ns` or `Nm`, default `5m`, at most `9m`; `--timeout 0` makes exactly one check (≤ 21 s). The whole wait is bounded by timeout + 21 s. Each check runs in a child process that is killed if it overruns.
|
|
167
|
+
- **`secrets manage [NAME...] [--scope S | --app ID] [--no-open]`** prints `{url, opened}` for the Secret Manager page, opening a browser unless `--no-open`.
|
|
168
|
+
|
|
169
|
+
**`wait` never signs in: it needs a stored session and doesn't accept `TC_PRIVATE_KEY`** (an explicit key exits 3). It never writes profile state, never saves a consent request, never prints a value and never prompts. After a CLI upgrade, run one normal command per profile first (for example `tc --profile agent auth caps`); otherwise the first `wait` returns `PROFILE_MIGRATION_REQUIRED` once, with that command in `error.recovery.command`.
|
|
170
|
+
|
|
171
|
+
| `wait` exit | Meaning |
|
|
172
|
+
|---|---|
|
|
173
|
+
| 0 | stdout `{status:"ready", names, scope?}` |
|
|
174
|
+
| 8 | `PENDING`: stdout `{status:"pending", pending, present, url}` (or `hint` instead of `url` for a local identity). Rerun `wait` without re-sending the link |
|
|
175
|
+
| 5 | `PERMISSION_DENIED`: `error.recovery` `{action:"request_access", names, owner, scope?}` names the denied names; `error.setup` covers names still pending |
|
|
176
|
+
| 3 | `AUTH_REQUIRED`: no stored session, an expired one, a 401, or `TC_PRIVATE_KEY` set. `error.recovery.command` is the persistent login to run once |
|
|
177
|
+
| 2 | usage, `OWNER_MISMATCH` |
|
|
178
|
+
| 6 | `NETWORK_ERROR` after repeated node failures; `error.progress` `{present, pending}` from the last successful check |
|
|
179
|
+
| 1 | `PROFILE_MIGRATION_REQUIRED` (run `error.recovery.command`, `tc --profile P auth caps`, once, then retry) or `PROBE_FAILED` |
|
|
180
|
+
| 130 | `CANCELLED` (SIGINT/SIGTERM); `error.progress` when a check had succeeded |
|
|
181
|
+
|
|
182
|
+
**Which identity gets a link (posture table):**
|
|
183
|
+
|
|
184
|
+
| Identity | `request` | missing secret on `get` / `wait` |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| `owner-openkey` (recorded owner) | link for the recorded owner | `error.setup` / `url` |
|
|
187
|
+
| `delegate-session` | link for the owner of its space (or `--owner`) | `error.setup` / `url` |
|
|
188
|
+
| key-only `did:key` with no recorded owner | `OWNER_DID_UNKNOWN` (3) unless `--owner` | `wait`: needs a session (3) |
|
|
189
|
+
| `local-owner-key`, a profile holding a `privateKey`, `--private-key`, `TC_PRIVATE_KEY` | `LOCAL_OWNER_PROFILE` (2) | hint `tc secrets put NAME --stdin`, no link |
|
|
190
|
+
|
|
191
|
+
**Error JSON fields** (stderr, JSON mode; only these keys ever appear in `error`: `code`, `message`, `hint`, `recovery`, `setup`, `progress`, `meta`, `warnings`):
|
|
192
|
+
- `error.setup` `{url, names, scope?, owner, action:"add_secret"}`: the canonical request link, on `NOT_FOUND` and on `wait`'s `PERMISSION_DENIED` for pending names.
|
|
193
|
+
- `error.recovery`: `{action:"sign_in", command?}` (a persistent login), `{action:"complete_migration", command?, operationId:"tinycloud.auth.capabilities"}`, or `{action:"request_access", names, owner, scope?}`. `command` is omitted for profile names that are not shell-safe; sign in to that profile, or run `auth caps` for it, yourself.
|
|
194
|
+
- `error.progress` `{present, pending}`: on `NETWORK_ERROR` and `CANCELLED` from `wait`, only after at least one successful check.
|
|
195
|
+
|
|
148
196
|
### Scoped secret reads for agents
|
|
149
197
|
|
|
150
198
|
An agent profile gets read access to named secrets through a scoped OpenKey paste login with a manifest that names them; [AUTH.md](AUTH.md#secret-reads-for-an-agent) has the full flow. Device login cannot grant secrets.
|
|
@@ -160,7 +208,7 @@ tc --profile agent secrets get OPENAI_API_KEY --raw
|
|
|
160
208
|
|
|
161
209
|
`secrets: { NAME: true }` requests, in the owner's `secrets` space, `kv/get` on `vault/secrets/NAME` and `capabilities/read` on `""`, plus the raw network entry `{ "service": "tinycloud.encryption", "space": "encryption", "path": "urn:tinycloud:encryption:<ownerDid>:default", "actions": ["tinycloud.encryption/decrypt"] }`. The owner DID comes from the profile's recorded owner, else `--owner`, and its address is EIP-55 checksummed in the URN; with neither the login fails with `OWNER_DID_UNKNOWN`. The JSON result lists approved `permissions` and `declined` entries. A decrypt signed inside the owner's space by an older OpenKey deployment is not a raw grant: scoped login reports it in `declined` and omits it from saved permissions. Browser `auth request --grant` or secret-read escalation refuses that old nested proof before activating or storing a grant (`OPENKEY_GRANT_BROADENED`); a renewed approval must sign the raw network grant.
|
|
162
210
|
|
|
163
|
-
When neither stdin nor stderr is a terminal, `secrets get|list|put|delete` on an OpenKey profile that lacks the grant fails with `PERMISSION_DENIED` (exit 5) and a scoped paste-login hint. Missing or expired sessions instead fail with `AUTH_REQUIRED` (exit 3) before a canonical read invokes the node or starts an unscoped browser refresh. On any profile kind, a stored
|
|
211
|
+
When neither stdin nor stderr is a terminal, `secrets get|list|put|delete` on an OpenKey profile that lacks the grant fails with `PERMISSION_DENIED` (exit 5) and a scoped paste-login hint. Missing or expired sessions instead fail with `AUTH_REQUIRED` (exit 3) before a canonical read invokes the node or starts an unscoped browser refresh. On any profile kind, a stored session that has expired (a signed session, or one `tc auth import` created from a compact UCAN) or no longer verifies fails with `AUTH_REQUIRED` (exit 3) and a sign-in hint before any node request, never as a retryable `NODE_ERROR`; operations and MCP report a non-retryable `AUTH_REQUIRED`. Any service result coded `AUTH_REQUIRED` or `PERMISSION_DENIED` without an HTTP status now exits 3 or 5 instead of 1 in commands using `cliErrorFromService` (account, delegation, DuckDB, encryption, KV, secrets, SQL, and vars); this preserves operation auth codes as typed errors.
|
|
164
212
|
|
|
165
213
|
## Local Replicas
|
|
166
214
|
|
|
@@ -304,9 +352,9 @@ On either recovery path, an orphaned `.recover-*` claim can hold a replacement o
|
|
|
304
352
|
- **Session key**: `did:key:z6Mk...#z6Mk...` — generated at init
|
|
305
353
|
- **Owner DID**: `did:pkh:eip155:{chainId}:{address}` — after auth
|
|
306
354
|
|
|
307
|
-
Use `--json` for machine-readable output; interactive commands can render human output. General command errors go to stderr as `{error: {code, message, hint?, meta?}}
|
|
355
|
+
Use `--json` for machine-readable output; interactive commands can render human output. General command errors go to stderr as `{error: {code, message, hint?, recovery?, setup?, progress?, meta?, warnings?}}` (`recovery`, `setup` and `progress` are described under [Secrets](#asking-the-owner-for-a-secret-request-wait-manage)). `meta` contains a typed HTTP `status` and, only when validated as a TinyCloud capability (and against the KV request when available), a `resource` and `requiredAction`. Inspect the structured code, not only the exit status.
|
|
308
356
|
|
|
309
|
-
General storage/auth exit codes: 0 success, 1 operation error, 2 invalid input, 3 authentication required, 4 not found, 5 permission denied, 6 network error, 7 node error, 10 storage full. `tc share` uses its own: 1 profile lock timeout or sender-history retry (`PROFILE_LOCK_TIMEOUT`, `SHARE_HISTORY_RETRY`), 3 upload authority required, 4 unavailable, expired or upload failed, 5 permission denied (`PERMISSION_DENIED`) or verification failed, 6 recipient authorization required or network error, 7 byte limit, 8 output conflict or unsafe filename, 9 partial success, 10 storage full.
|
|
357
|
+
General storage/auth exit codes: 0 success, 1 operation error (including `PROFILE_MIGRATION_REQUIRED` and `PROBE_FAILED`), 2 invalid input, 3 authentication required, 4 not found, 5 permission denied, 6 network error, 7 node error, 8 pending (`tc secrets wait` only), 10 storage full, 130 interrupted (`CANCELLED`). `tc share` uses its own: 1 profile lock timeout or sender-history retry (`PROFILE_LOCK_TIMEOUT`, `SHARE_HISTORY_RETRY`), 3 upload authority required, 4 unavailable, expired or upload failed, 5 permission denied (`PERMISSION_DENIED`) or verification failed, 6 recipient authorization required or network error, 7 byte limit, 8 output conflict or unsafe filename, 9 partial success, 10 storage full.
|
|
310
358
|
|
|
311
359
|
For `tc kv get|put|head|list|delete`, `tc space list|host`, `tc sql`, `tc vault`, `tc vars`, `tc duckdb`, `tc secrets list|put|delete`, `tc delegation`, and `tc account`, service-result errors use typed authorization: a plain HTTP 401 is `AUTH_REQUIRED` (exit 3); a 403 is `PERMISSION_DENIED` (exit 5). A 401 with a validated missing capability, or `AUTH_UNAUTHORIZED` without an HTTP status, is `PERMISSION_DENIED` (exit 5); a validated capability also produces a `tc auth request --cap` hint. This changes the former exit 1 for status-less `AUTH_UNAUTHORIZED` in the listed commands. Other HTTP failures retain their service-specific codes. `tc share` has separate exit mappings below.
|
|
312
360
|
|