@tinycloud/cli 1.2.0-beta.0 → 1.2.0-beta.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinycloud/cli",
3
- "version": "1.2.0-beta.0",
3
+ "version": "1.2.0-beta.1",
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.0",
34
+ "@tinycloud/node-sdk": "3.2.0-beta.1",
35
35
  "@tinycloud/node-sdk-wasm": "1.7.7",
36
36
  "@tinycloud/sdk-core": "3.2.0-beta.0",
37
- "@tinycloud/operations": "0.5.0-beta.0",
37
+ "@tinycloud/operations": "0.5.0-beta.1",
38
38
  "chalk": "^5.3.0",
39
39
  "commander": "^14.0.0",
40
40
  "open": "^11.0.0",
@@ -90,7 +90,7 @@ 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. This needs a signed session: a delegate session created by `tc auth import` stores no SIWE or expiry, so its expiry is detected only when the node refuses it.
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
96
  ## More spaces after login
@@ -110,6 +110,7 @@ Delegations stored in a profile's `additional-delegations.json` replay only into
110
110
  - **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
111
  - **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
112
  - **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.
113
+ - **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
114
  - **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
115
  - 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
116
  - **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 +133,7 @@ To replace an old grant without a proof, an owner can approve again with `tc aut
132
133
 
133
134
  `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
135
 
135
- Only an explicit `--host` becomes the profile's stored host; `TC_HOST` and a discovered local node apply to that command only.
136
+ 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
137
 
137
138
  `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
139
 
@@ -160,7 +160,7 @@ tc --profile agent secrets get OPENAI_API_KEY --raw
160
160
 
161
161
  `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
162
 
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 signed session that has expired 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, kv, secrets list/put/delete, space, sql, vars and vault); for example, `tc secrets list` without a grant returns the NodeSecretsService's status-less `PERMISSION_DENIED` with exit 5. `tc secrets get` and `tc auth import` additionally supply the profile kind's sign-in hint for `AUTH_REQUIRED`. A delegate session bootstrapped by `tc auth import` stores no SIWE or expiry, so its expiry is found only when the node refuses it. At a terminal, the owner approves a missing grant in OpenKey: the request adds `tinycloud.capabilities/read` on the space root and keeps raw decrypt in the `encryption` pseudo-space, and a decrypt-only request is placed in the secrets space. The grant is stored with its signed SIWE proof and bound to exactly its signed capabilities, and `secrets get` (like every operation, including MCP) re-verifies it against the session key and owner, and holds it to that binding, in the same process or a new one; a stored grant that fails (such as one from an earlier release, stored without a proof) is skipped and reported as a `STORED_GRANT_SKIPPED` warning with a fixed reason code and its CID when that CID is the CID of the stored bytes: inside the JSON error's `warnings` (including a failed `-o` write), as one `{"warnings": [...]}` stderr line after a successful read's output, or as one human-readable stderr line. `--json` selects the JSON forms even with stdout on a terminal, and suppresses progress spinners. Expired grants are dropped silently. Operations and MCP results carry the same `warnings` array. A JSON error can carry both `meta` (validated authorization fields) and `warnings`. A canonical `secrets get` that still lacks authority fails with `PERMISSION_DENIED` (exit 5). Redirected stdout alone does not disable an owner's browser approval; its URL appears on stderr and terminal stdin accepts a code. `secrets get -o` validates the destination and existing directory before any read or decrypt, then syncs a fresh 0600 inode and atomically replaces an existing regular file. Directory sync is best-effort after replacement and cannot turn a completed write into a reported failure. It refuses symlinks, directories, devices and absent parent directories (`INVALID_ARGUMENT`); an unreplaceable filesystem target fails with a sanitized error naming the destination, without a non-atomic fallback. A node that refuses decrypt (HTTP 401/403) is reported as missing authority on the invoked network, not as an undecryptable secret.
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 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
164
 
165
165
  ## Local Replicas
166
166