@hediet/linkrpc-cli 0.0.3-next.20261008.2 → 0.0.3-next.20261021.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 CHANGED
@@ -9,6 +9,284 @@ interfaces (`hubrpc.defaults`, `hubrpc.directory`, `hubrpc.schemas`) are
9
9
  used to discover what the endpoint exposes. Generic RPC commands work with any
10
10
  reflected interface; compiled-in views add specialized CLI and TUI renderers.
11
11
 
12
+ ## Daemon-owned endpoints (`hub services`)
13
+
14
+ `hub services` manages processes and existing connections **through the daemon
15
+ management RPC API**, including on the local machine. The CLI may bootstrap a
16
+ local per-user daemon, but never supervises the managed workload itself.
17
+
18
+ The daemon executable and implementation are bundled in this package's `dist/`
19
+ (`daemon.js`). Installing the CLI does not require publishing or installing
20
+ `@hediet/linkrpc-hub-daemon` separately. The daemon source package is a build-only
21
+ workspace dependency; bootstrap launches the bundled executable with Node.js.
22
+
23
+ Before using a daemon, the CLI checks its `requiredInterfaces` against the
24
+ interface IDs and hashes advertised through reflection. Each required hash must
25
+ be supported; no package/protocol version or automatic update policy is used.
26
+ A legacy daemon without reflection, a missing interface, or a different hash
27
+ fails with an actionable error before the workload is created:
28
+
29
+ ```sh
30
+ hub services restart
31
+ ```
32
+
33
+ This explicit command sends the local daemon's management `shutdown` RPC,
34
+ waits for exit, starts the CLI's bundled daemon, and rechecks its interfaces.
35
+ It disconnects Hub clients and stops workloads. Ephemeral endpoints disappear;
36
+ persistent definitions retain their existing restart/desired-state semantics.
37
+ It does **not** modify OS autostart registrations. A selected endpoint/config or
38
+ routed manager is never silently replaced: update it on its execution host, or
39
+ explicitly select the default local daemon with
40
+ `hub --context :empty --no-use-env services restart`.
41
+
42
+ OS autostart integration for `hub services` is postponed. Bootstrap/restart
43
+ does not invoke the OS service manager, and persistent endpoint creation does
44
+ not enable login startup. Existing registrations are left untouched and may
45
+ still start a different build at the next login.
46
+
47
+ ```sh
48
+ # Create a context explicitly and run attached (daemon-owned lifetime):
49
+ hub services run --new-context . -- node server.js
50
+
51
+ # Bind the service lifetime to this terminal's CLI process:
52
+ hub services run --foreground --id temporary -- node server.js
53
+
54
+ # Another terminal in this context:
55
+ hub services list
56
+ hub ls
57
+ hub services logs --follow
58
+
59
+ # Detach an attached pipe console with Ctrl+D on an empty line.
60
+ # Or launch detached from the outset:
61
+ hub services run --id worker --detach -- node worker.js
62
+ hub services attach --id worker
63
+ hub services stop --id worker
64
+
65
+ # Run in a pseudoterminal (TTY); detach with Ctrl+B, then d:
66
+ hub services run -x -t --id shell -- pwsh
67
+
68
+ # Stop and replace an existing endpoint with the same ID:
69
+ hub services run --force --id worker --detach -- node worker.js
70
+
71
+ # Retain a definition and resume it when the daemon next starts:
72
+ hub services run --id worker --persistent --restart on-failure --detach -- node worker.js
73
+ hub services configure --id worker --restart always
74
+ hub services stop --id worker # stays stopped across daemon restarts
75
+ hub services start --id worker
76
+ hub services remove --id worker
77
+
78
+ # Connect rather than spawn (URI is interpreted on the daemon host):
79
+ hub services connect --id peer --target-endpoint ws://localhost:7000 --restart on-failure
80
+ ```
81
+
82
+ `--id` defaults to `main`. Namespace defaults to the selected context's canonical
83
+ folder path (case-normalized on Windows), its named context identity (`id:<name>`),
84
+ or `:root` where no folder context applies. The `:empty` context has no namespace.
85
+ Override it with `hub services --namespace <name> ...`. `--namespace` belongs
86
+ only to `services`; it does not select or filter services for `call`, `ls`, or
87
+ `ui`. `--context` only selects an existing context;
88
+ `--new-context .` creates one before the attached command starts. A namespace
89
+ does not determine a remote working directory.
90
+
91
+ Definitions default to ephemeral: they disappear after stop, completion, or
92
+ terminal failure. `--persistent` retains a definition. `--restart` is `never`
93
+ (default), `on-failure`, or `always`; for connections, disconnect counts as
94
+ failure. Persistent restarting endpoints resume when the daemon next starts,
95
+ unless explicitly stopped. This does not arrange for the daemon itself to start
96
+ after OS restart. Persistent `never` definitions remain stopped after daemon
97
+ restart.
98
+
99
+ Endpoint selection keeps the usual explicit flag / environment / context
100
+ precedence, including `HUBRPC_*` aliases. An explicit `--context` suppresses the
101
+ ambient endpoint unless `--use-env` is passed. On an effective Hub connection,
102
+ the manager defaults to `<hubServiceId>/daemon`; override with
103
+ `--daemon-service`. Both that option and `--namespace` can be saved using
104
+ `context set`. An unreachable or unauthorized selected manager is an error,
105
+ **never** a fallback to local execution.
106
+
107
+ ```sh
108
+ hub --endpoint ws://remote-host:7000 --daemon-service remote/daemon \
109
+ --context . services run --id worker --cwd /srv/project --detach -- node worker.js
110
+ ```
111
+
112
+ Remote launches require `--cwd` on the execution host. Commands, files, and
113
+ environment values are not uploaded. Arguments after `--` are an executable
114
+ and argv, not a shell command string. Execution host, namespace, ID, command,
115
+ and working directory are printed before creation. Local fallback is used only
116
+ when no endpoint/config is selected. `run`, `connect`, and `start` may bootstrap
117
+ the local daemon; reads never do. Without a selected endpoint, `hub ls`, `call`,
118
+ and other connected commands (including `ui`) use the local Hub whenever its
119
+ daemon runs, whether or not any services do. If it is not running, the command
120
+ reports an error without starting the daemon. A configured endpoint that fails
121
+ never falls back to the local Hub.
122
+ Use the full `serviceId` returned by `services list` for RPC
123
+ calls; endpoint IDs and published RPC service IDs are different things.
124
+
125
+ Process readiness defaults to connecting to the provided `LINKRPC_ENDPOINT`
126
+ and token. Use `--no-rpc` (short form `-x`) for non-LinkRPC programs: they become
127
+ ready on spawn, without a participant socket or managed identity. Ambient
128
+ `LINKRPC_ENDPOINT`/`LINKRPC_TOKEN` and legacy `HUBRPC_*` connection variables
129
+ are not inherited in this mode; explicitly supplied `--env` values are preserved.
130
+ `--readiness spawned` alone still provisions RPC, but does not wait for connection.
131
+ `--no-rpc --readiness connected` is an error. Detached launches
132
+ wait up to 30 seconds for readiness; a timeout leaves the endpoint managed and
133
+ inspectable. Attachments stream bounded recent output and supervisor notices,
134
+ forward input and interrupts, and do not own the process lifetime. `logs`
135
+ prints recent output; `logs --follow` keeps streaming; `attach --read-only`
136
+ observes without sending input.
137
+
138
+ Every process endpoint has a console that implements the standard
139
+ [`linkrpc.console.v1`](../linkrpc-infra/README.md#process-consoles-linkrpcconsole)
140
+ interface template: the daemon's management interface contains one template
141
+ instance, `consoles`, whose params are the endpoint address. `hub services attach`
142
+ calls it directly. The generic `attach` command works with **any** participant
143
+ that instantiates the template, not only daemon-managed processes. It discovers
144
+ consoles through the `linkrpc.console` directory tag and reads the wire methods
145
+ and params schema from reflection:
146
+
147
+ ```sh
148
+ hub attach # list consoles and their params schemas
149
+ hub attach hub/daemon --params '{"namespace":"d:\\work\\app","id":"main"}'
150
+ rpc --endpoint ws://localhost:7000 attach my/service --read-only
151
+ ```
152
+
153
+ `--params` (default `{}`) is validated against the instance's reflected params
154
+ schema. When a service has several console instances, select one with
155
+ `--instance` or `--interface`.
156
+
157
+ #### Pipe and TTY consoles
158
+
159
+ Processes default to a **pipe console**: stdout and stderr stay separate, and
160
+ the program sees no terminal. `-t` / `--tty` runs it in a pseudoterminal
161
+ (ConPTY on Windows) instead: output is a single terminal stream, programs enable
162
+ colors, prompts, and full-screen interfaces, and attachments can resize it. The
163
+ TTY adopts the size of the most recent interactive attachment. Joining
164
+ attachments receive the **current screen** (rendered by a headless terminal in
165
+ the daemon, including scrollback) instead of a raw byte replay, so full-screen
166
+ programs display correctly.
167
+
168
+ | | Pipe console | TTY console (`-t`) |
169
+ | --- | --- | --- |
170
+ | Local input | Line editing; Enter sends the line | Raw: every key goes to the program |
171
+ | Ctrl+C | Interrupts (sends SIGINT where supported), or stops a foreground owner | Goes to the program (an interrupt in a shell) |
172
+ | Ctrl+D | On an empty line: detach, or stop a foreground owner | Goes to the program (EOF in a shell) |
173
+ | Detach | Ctrl+D on an empty line (Ctrl+] also works) | The detach keys: Ctrl+B, then d by default |
174
+
175
+ TTY detach keys use Docker's `--detach-keys` syntax: comma-separated keys, each
176
+ a character or `ctrl-<key>` (`a`-`z`, `@`, `[`, `\`, `]`, `^`, `_`). The default
177
+ is `ctrl-b,d`, tmux's binding. Docker's `ctrl-p,ctrl-q` does not work in VS
178
+ Code's integrated terminal, which handles Ctrl+P (Quick Open) and, on Windows,
179
+ Ctrl+Q itself; Ctrl+B passes through. Keys of an incomplete sequence are held
180
+ back and forwarded unchanged when the sequence does not complete. Change the
181
+ sequence per command or store it in a context:
182
+
183
+ ```sh
184
+ hub --detach-keys ctrl-p,ctrl-q services attach --id shell
185
+ hub context set --detach-keys 'ctrl-],q' # store it in the active context
186
+ ```
187
+
188
+ Choose another sequence when the attached program uses Ctrl+B itself, for
189
+ example tmux running inside the console.
190
+
191
+ Windows pipes have no interrupt channel: there, Ctrl+C in a pipe attachment
192
+ prints a hint instead of terminating the shared process; use `-t` when the
193
+ program should receive Ctrl+C. When a TTY attachment ends, the CLI resets
194
+ terminal modes the program may have enabled (mouse, bracketed paste, alternate
195
+ screen). Piped input is forwarded as received; EOF on a pipe does not cancel the
196
+ attachment or stop the service, so it can continue producing output.
197
+
198
+ The attached-console banner prints a `hub services attach ...` command for
199
+ reattaching, and detaching prints it again. Duplicate `run`/`connect` errors
200
+ print the same recovery command without stopping or replacing the existing
201
+ endpoint, and point to `--force`. Hints include the selected
202
+ context, namespace, endpoint ID, environment policy, and explicit connection
203
+ overrides. Reuse the same environment when `--use-env` is shown and the same
204
+ working directory for relative connection/config paths. Explicit tokens and
205
+ command-environment values are replaced with `<same ...>` placeholders rather
206
+ than exposed in the hint.
207
+
208
+ `--force` stops an existing endpoint with the same address and replaces it with
209
+ the new definition in one daemon operation. This also applies to persistent
210
+ endpoints (their stored definition is replaced) and to a foreground endpoint
211
+ owned by another terminal: that terminal prints a notice and its command ends.
212
+
213
+ `run --foreground` instead creates the process inside a lifetime-owning
214
+ management RPC. Ctrl+C, SIGTERM, cancellation, or a detected CLI disconnect
215
+ causes the daemon to stop the process tree and remove the endpoint, even when
216
+ the CLI is forcibly killed and cannot run an exit handler. On a remote Hub,
217
+ cleanup begins once the disconnect is detected. The normal graceful stop
218
+ timeout applies when a stop request can be delivered: SIGTERM on POSIX or
219
+ disconnecting an active RPC connection. On Windows, a process without an
220
+ RPC connection has no graceful stop channel, so its tree is terminated
221
+ immediately instead of waiting out an idle five-second timeout.
222
+
223
+ Other terminals can discover/call RPC services and attach as observers; closing
224
+ an observer does not stop it. The detach key of the owning terminal (Ctrl+D on
225
+ an empty line, or the TTY detach keys) cancels the foreground command and
226
+ therefore stops the service, rather than detaching it.
227
+ `--foreground` conflicts with `--detach` and `--persistent`; the management API
228
+ also rejects making a foreground endpoint persistent later. Restart policies
229
+ apply only while the owning call is alive. Hub replacement stops foreground
230
+ endpoints rather than restoring them without an owner. Ordinary `run` and
231
+ `attach` retain their daemon-owned lifetime behavior.
232
+
233
+ With a pipe console, interactive terminals use local line editing: with
234
+ unfinished text, Ctrl+D retains the line and uses normal delete-under-cursor
235
+ behavior.
236
+
237
+ #### Plain stdio programs
238
+
239
+ The [counter example](examples/counter.mjs) prints one number per second and has
240
+ no LinkRPC dependency. From this package directory, with an existing context:
241
+
242
+ ```sh
243
+ # Background: the counter survives the launching CLI.
244
+ hub services run -x --id counter --detach -- node examples/counter.mjs
245
+ hub services attach --id counter
246
+ # Ctrl+D on an empty line detaches without stopping the counter.
247
+ hub services logs --id counter --follow
248
+ hub services stop --id counter
249
+
250
+ # Foreground: closing this CLI stops and removes the counter.
251
+ hub services run --no-rpc --foreground --id counter -- node examples/counter.mjs
252
+ # A different terminal can still attach while the owner is running:
253
+ hub services attach --id counter
254
+ ```
255
+
256
+ The [interactive counter](examples/interactive-counter.mjs) also accepts input.
257
+ It prints `tick 1`, `tick 2`, ... once per second and responds to `count`,
258
+ `reset`, `echo <text>`, and `quit`:
259
+
260
+ ```sh
261
+ hub services run -x --foreground --id interactive -- node examples/interactive-counter.mjs
262
+ # Type: count, echo hello, reset, or quit (then Enter).
263
+ # Or start with --detach instead of --foreground and attach separately:
264
+ hub services attach --id interactive
265
+ ```
266
+
267
+ `count` prints the current number, `reset` sets it to zero, `echo hello` prints
268
+ `[<current count>] hello`, and `quit` exits the service. Requests can come from
269
+ any attachment; all attachments see the responses and ticks.
270
+
271
+ Create a context first with `hub context --new-context .` if needed. Multiple
272
+ attachments receive the same output; interactive attachments share the process's
273
+ stdin. Use `logs --follow` for a read-only observer. Restart/persistence policies
274
+ work for plain processes too.
275
+
276
+ `services list` reports `rpc: false` for these processes. They do not publish
277
+ application RPC methods under their reserved `serviceId`: other Hub participants
278
+ attach to their console (`hub services attach`, or generically through the
279
+ daemon's `consoles` template instance) to observe output and
280
+ send input, in both foreground and background mode. When the program adds
281
+ LinkRPC support, omit `-x` to provision its RPC connection. Add `-t` when the
282
+ program expects a terminal.
283
+
284
+ Connections support initialized LinkRPC socket/WebSocket endpoints. Forwarding
285
+ preserves method names and signatures, rather than rewriting root services
286
+ under a new prefix. The target must serve the assigned namespace (available
287
+ from `services list` or by querying the Hub's granted namespace over the
288
+ bidirectional connection). Connecting does not own or stop the target process.
289
+
12
290
  ## Views
13
291
 
14
292
  Views target an **interface** or a **service** (including the main/root service),
@@ -183,6 +461,36 @@ linkrpc hub --endpoint ws://localhost:7700 call app::api::get
183
461
  linkrpc hub call app::api::get --endpoint ws://localhost:7700
184
462
  ```
185
463
 
464
+ Profiles are presets, not restrictions on shared call behavior. Signing defaults
465
+ to off in `linkrpc`/`rpc` and on in `linkrpc hub`/`rpc hub`/`hub`.
466
+ Use `--sign` or `--no-sign` to override that default. `--principal` chooses
467
+ the identity; it does not by itself enable signing. Signed calls present stored
468
+ grants; unsigned calls do not load or present the capability bag.
469
+ `caps` management is available only in the Hub profile, but shared `call`
470
+ supports `--cap:request` and the `--cap:allow-*` options in both profiles.
471
+
472
+ Setting precedence is explicit CLI overrides, then enabled environment
473
+ overrides, then saved context values, then profile defaults. Environment
474
+ overrides currently supply endpoint/token settings, not signing settings.
475
+ `--sign`/`--no-sign` are saved with `context set` or `--context-set` and can be
476
+ removed with `context set --unset sign`. The existing environment-selection
477
+ policy remains: an explicit `--context` suppresses the Hub's default environment
478
+ override unless `--use-env` is supplied.
479
+
480
+ `--cap:request` enables signing when the signing setting is unspecified; it
481
+ does not change validation/environment defaults or switch command profiles.
482
+ An explicit or saved `sign: false` conflicts with capability operations;
483
+ override it with `--sign`. `--cap:request --validation off` is also rejected.
484
+ These calls illustrate equivalent settings:
485
+
486
+ ```sh
487
+ linkrpc hub call files::fs::read --cap:request --p:path=/docs/a
488
+ linkrpc call files::fs::read --cap:request --validation required --use-env --p:path=/docs/a
489
+
490
+ # Reuse stored grants through the generic profile, without requesting:
491
+ linkrpc call files::fs::read --sign --validation required --p:path=/docs/a
492
+ ```
493
+
186
494
  Use `--validation auto|required|off` to override a profile default.
187
495
 
188
496
  ## Contexts
@@ -190,15 +498,19 @@ Use `--validation auto|required|off` to override a profile default.
190
498
  Contexts persist command defaults in the global user configuration store. A
191
499
  folder context is keyed by its canonical folder path; no file is written into
192
500
  that folder. Without `--context`, the CLI searches from the current directory
193
- through its parents, then uses `:root`, then the immutable `:empty` context.
501
+ through its parents, then uses `:root`.
194
502
 
195
503
  Context selectors are:
196
504
 
197
505
  - An absolute or relative folder path.
198
506
  - `id:<name>` for a named context.
199
507
  - `:root` for the system-wide root above platform filesystem roots. On Unix,
200
- `/` is equivalent to `:root`.
201
- - `:empty` for no stored defaults.
508
+ `/` is equivalent to `:root`. The root context **always exists**: until you
509
+ store defaults in it, it has none. It cannot be created with `--new-context`,
510
+ and removing it resets it to no defaults. Because it applies to every folder
511
+ without its own context, `context set` and `--context-set` change it only when
512
+ selected explicitly (`--context :root`).
513
+ - `:empty` for no stored defaults, selected only explicitly. It is immutable.
202
514
 
203
515
  `LINKRPC_CONTEXT` selects a context when `--context` is absent;
204
516
  `HUBRPC_CONTEXT` is its legacy alias. An explicit `--context` also suppresses
@@ -206,8 +518,8 @@ the Hub profile's normal environment endpoint overlay. Every context value can
206
518
  still be overridden by a command-line option.
207
519
 
208
520
  ```sh
209
- # Set endpoint and principal on the nearest context, creating one for cwd if needed.
210
- hub context set --endpoint 'wss://hub.example?token=%' \
521
+ # Explicitly create a context for cwd with endpoint and principal defaults.
522
+ hub context set --new-context . --endpoint 'wss://hub.example?token=%' \
211
523
  --endpoint-token secret --principal user:work
212
524
 
213
525
  # Create a named context from the effective values only after the call succeeds.
@@ -217,14 +529,25 @@ hub call app::api::get --new-context id:working-copy
217
529
  hub call app::api::get --validation off --context-set
218
530
 
219
531
  hub context # same as `hub context show`
532
+ hub context create id:working-copy # same as: hub context --new-context id:working-copy
220
533
  hub context resolve --context id:working-copy
221
- hub context list
534
+ hub context list # always includes :root
222
535
  hub context remove --context .
536
+ hub context set --context :root --principal user:work # defaults for folders without a context
537
+ hub context remove --context :root # resets :root to no defaults
223
538
  hub call app::api::get --context :empty --endpoint ws://localhost:7700
224
539
  ```
225
540
 
226
- Context creation and mutation are explicit: use `context set`,
227
- `--context-set`, or `--new-context <selector>`. Context values, including
541
+ Context creation is explicit: use `hub context create <selector>` or
542
+ `--new-context <selector>` (for example, `hub context --new-context .` creates a
543
+ folder context from effective defaults and shows it).
544
+ `--context <selector>` only selects an existing context; missing-context errors
545
+ show how to create it. `context set` and `--context-set` update existing contexts
546
+ and do not implicitly create a cwd context. Use `context set --new-context <selector>`
547
+ to create one with initial defaults. Combining `--context existing` with
548
+ `--new-context new` derives the new context from an existing one; it never creates
549
+ the selected source context.
550
+ Context values, including
228
551
  endpoint tokens, are currently stored inline and unencrypted. Principals are
229
552
  stored by selector (`managed`, `user:<id>`, or `file:<path>`); their private
230
553
  keys remain in the existing identity store.
@@ -260,6 +583,7 @@ Every endpoint is a valid `new URL()` — safe in env vars and logs:
260
583
 
261
584
  | URI | Transport |
262
585
  | ----------------------------------------- | ------------------------------------------ |
586
+ | `linkrpc:local` | CLI discovery of the current user's local LinkRPC Hub |
263
587
  | `unix:/run/hub/hub.sock?token=…` | unix-domain socket |
264
588
  | `npipe://./pipe/linkrpc?token=…` | Windows named pipe |
265
589
  | `ws://host:7700?token=…` / `wss://…` | LinkRPC WebSocket (token → `hubrpc::initialize`) |
@@ -267,6 +591,23 @@ Every endpoint is a valid `new URL()` — safe in env vars and logs:
267
591
  | `cmd:?command=…` / `cmd:?argv=…&argv=…` | spawn-as-server (`--endpoint-cmd`) |
268
592
  | `cmd-stdio:?command=…` / `?argv=…&argv=…` | spawn over stdio (`--endpoint-cmd-stdio`) |
269
593
 
594
+ `linkrpc:local` resolves the local daemon's application endpoint and credentials
595
+ from its discovery file, honoring `LINKRPC_HUB_HOME`. It never starts the daemon
596
+ and works even when no managed services are running. It is a CLI lookup URI,
597
+ not a wire transport or a service filter; raw SDK endpoint parsers do not resolve
598
+ it. It works as a CLI flag, a saved context endpoint, or an endpoint environment
599
+ variable. Explicit selection overrides ambient endpoint settings:
600
+
601
+ ```sh
602
+ hub --endpoint linkrpc:local ui
603
+ hub --endpoint linkrpc:local ls
604
+ hub context --new-context . --endpoint linkrpc:local
605
+ ```
606
+
607
+ Credentials come exclusively from discovery; `--endpoint-token` is rejected for
608
+ this URI. No username-derived pipe address needs to be hardcoded. By contrast,
609
+ the implicit local fallback requires at least one running managed endpoint.
610
+
270
611
  `cmd:` children can use `startEndpoint({ onConnection })` without parsing any
271
612
  transport flags: the parent injects `LINKRPC_ENDPOINT` and `LINKRPC_TOKEN`.
272
613
  The CLI accepts one authenticated callback connection and exposes the child's
@@ -291,6 +632,12 @@ LINKRPC_ENDPOINT=unix:/run/hub/hub.sock?token=abc linkrpc ls
291
632
 
292
633
  ## Commands
293
634
 
635
+ ### Consoles
636
+
637
+ | Command | Purpose |
638
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------- |
639
+ | `attach [serviceId] [--params <json>] [--instance <name>] [--interface <id>] [--read-only]` | Attach to a [`linkrpc.console.v1`](../linkrpc-infra/README.md#process-consoles-linkrpcconsole) template instance of any service, found through the `linkrpc.console` tag; without a service, list consoles. Pipe consoles use line editing (Ctrl+D on an empty line detaches); TTY consoles receive raw keys (`--detach-keys`, default Ctrl+B, then d, detaches). |
640
+
294
641
  ### Reflection
295
642
 
296
643
  | Command | Purpose |
@@ -446,6 +793,97 @@ frame, and each remote frame is written as one JSON line:
446
793
  hub --endpoint unix:/run/hub.sock json-rpc-stdio language-server
447
794
  ```
448
795
 
796
+ ### Capabilities
797
+
798
+ `linkrpc hub caps list [--json]` lists the selected principal's stored capabilities,
799
+ including expired grants. `caps request <methodRef>` requests and stores a
800
+ capability without executing the method. `call --cap:request` ensures the same
801
+ scope is available before executing the call. These explicit operations enable
802
+ signing even for generic-profile `call` and require a LinkRPC endpoint.
803
+ Without `--cap:request`, `call` never requests new capabilities (including
804
+ reflection access). When signing is enabled, it signs with the selected
805
+ identity and presents stored grants. Generic calls remain unsigned unless
806
+ `--sign` or a saved signing setting enables signing.
807
+ Missing or unusable grants fail with guidance to retry using `--cap:request`;
808
+ unguarded methods and plain RPC endpoints remain usable without capabilities.
809
+
810
+ Both request forms share `call`'s method references, `--params`, `--param`, and
811
+ `--p:<name>` shortcuts, including nested argument overrides and dynamic help.
812
+ Concrete arguments become exact constraints by default. Repeat
813
+ `--cap:allow-param 'key=<matcher JSON>'` to relax individual **top-level** parameters:
814
+
815
+ ```sh
816
+ linkrpc hub --endpoint "$ENDPOINT" caps list --json
817
+
818
+ # Request permission without reading the file:
819
+ linkrpc hub --endpoint "$ENDPOINT" caps request files::fs::read \
820
+ --p:path=/workspace/readme.txt \
821
+ --cap:allow-param 'path={"prefix":"/workspace/"}'
822
+
823
+ # Ensure permission for that scope, then read a file:
824
+ linkrpc --endpoint "$ENDPOINT" call files::fs::read --cap:request \
825
+ --p:path=/workspace/another.txt \
826
+ --cap:allow-param 'path={"prefix":"/workspace/"}'
827
+ ```
828
+
829
+ Matchers are `{"exact":value}`, `{"enum":[value,...]}`,
830
+ `{"prefix":"string-prefix"}`, `{"subsetOf":["allowed","array","items"]}`,
831
+ or `{"any":true}`. Constraints must be compatible with the reflected,
832
+ Zod-derived method schema, and the concrete example arguments must satisfy
833
+ both that schema and every constraint. Required and unknown keys are checked.
834
+ Nested matcher paths are unsupported by the capability protocol.
835
+ `any` allows every JSON value, so it is accepted only for unconstrained schema
836
+ properties; use `prefix`/`enum`/`subsetOf` for typed properties. A prefix cannot
837
+ be proven compatible with a finite enum or a formatted string; use an enum
838
+ of valid values instead. Remote reflection cannot reproduce arbitrary
839
+ producer-side custom Zod refinements.
840
+
841
+ Instead of naming the key in `--cap:allow-param`, attach a shortcut immediately
842
+ after a `--param key=value` or `--p:key=value` assignment:
843
+
844
+ ```sh
845
+ linkrpc hub call files::fs::read --cap:request \
846
+ --p:path=/workspace/readme.txt --cap:allow-prefix=/workspace/
847
+ ```
848
+
849
+ | Shortcut | Matcher | Meaning |
850
+ | --- | --- | --- |
851
+ | `--cap:allow-prefix <string>` | `prefix` | Any string beginning with the prefix |
852
+ | `--cap:allow-any` | `any` | Any JSON value; requires an unconstrained schema property |
853
+ | `--cap:allow-enum <JSON array>` | `enum` | One entire value from a non-empty array of alternatives |
854
+ | `--cap:allow-subset <JSON string array>` | `subsetOf` | An array containing only allowed strings; empty arrays, repetitions, and any ordering are allowed |
855
+
856
+ Each parameter can have only one explicit constraint. Mixing shortcuts with
857
+ `--cap:allow-param` for the same key, repeating constraints, or reassigning a
858
+ constrained parameter is an error. Shortcuts do not attach to `--params` JSON
859
+ or across intervening options. With variadic `--param`, a shortcut attaches
860
+ to the last assignment. The same schema and example-value validation applies
861
+ to shortcuts and explicit matchers.
862
+
863
+ Value-taking options support both `--option=value` and `--option value`.
864
+ Parameter assignment itself still uses `key=value`, e.g.
865
+ `--param path=/docs/a` or `--param=path=/docs/a`. Use the equals form for
866
+ dash-prefixed values. Quote JSON and strings containing spaces.
867
+ `--cap:request` and `--cap:allow-any` are switches with no value.
868
+ The older `--request-cap`, `--allow-param`, and `--duration` spellings have
869
+ been replaced by the `--cap:` options.
870
+
871
+ Schema validation is mandatory for explicit requests: `--no-validate` and
872
+ `--validation off` are rejected. On `call`, `--cap:allow-param` requires
873
+ `--cap:request`. For explicit requests, a method reference's `@hash` also pins
874
+ the permission; signed calls include that hash in their signed metadata.
875
+ The default requested duration is `persistent`; `--cap:duration shortLived` and
876
+ `--cap:duration longLived` are also supported. The Hub still decides what to grant.
877
+ One-shot approval cannot satisfy a reusable scope and is rejected.
878
+
879
+ Fresh durable grants for the selected principal are reused when they cover
880
+ the **entire requested scope**, not merely the example call. Broader prefixes,
881
+ enum sets, subset sets, and unconstrained grants can cover narrower requests.
882
+ New grants are saved through the principal's existing capability bag and are
883
+ available to later invocations using the same identity. Denial or an insufficient
884
+ grant fails without executing the method. `caps request` outputs the granted
885
+ capabilities and a `reused` flag; `call --cap:request` retains normal call output.
886
+
449
887
  ### Identity and approvals
450
888
 
451
889
  These commands resolve the same persistent identity selected by the global
@@ -474,6 +912,12 @@ only when the selected identity is a configured trusted root.
474
912
  Discovery diagnostics are written to stderr, including every manifest source
475
913
  and its pending-request count; `--json` stdout remains machine-readable.
476
914
 
915
+ Hub calls (`call`, `ls`, …) additionally present a self-issued capability for
916
+ every call. A Hub accepts it only where the identity is a configured trusted
917
+ root, so a root needs no approval for its own calls; elsewhere it is ignored
918
+ and stored or requested grants apply as before. A Hub answers a trusted root's
919
+ access request at once, without parking it for approval.
920
+
477
921
  ```sh
478
922
  hub --endpoint unix:/run/hub.sock?token=abc identity show --json
479
923
  hub --endpoint unix:/run/hub.sock?token=abc approval requests --json