@ariestools/cli 0.1.19 → 0.1.22

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
@@ -1,6 +1,6 @@
1
1
  # @ariestools/cli
2
2
 
3
- Aries Tools CLI — a suite by Arie Trouw for AI-client tooling, sandboxed command execution, XL1 datalake provisioning, content hashing (strict + fuzzy + perceptual), XL1 wallet management, payload witnessing, and XYO protocol utilities.
3
+ Aries Tools CLI — a suite by Arie Trouw for AI-client tooling, XL1 datalake provisioning, content hashing (strict + fuzzy + perceptual), XL1 wallet management, payload witnessing, and XYO protocol utilities.
4
4
 
5
5
  ## Installation
6
6
 
@@ -15,8 +15,10 @@ pnpm add -g @ariestools/cli
15
15
  ```sh
16
16
  aries --help
17
17
  aries info
18
- aries hash --algorithm pdq ./photo.jpg
18
+ aries secrets status
19
+ ariesi hash --algorithm pdq ./photo.jpg
19
20
  ariesi plex libraries
21
+ ariesi gate pair ticker BTC_USDT
20
22
  aries wallet create
21
23
  aries witness url --url https://example.com
22
24
  aries datalake list
@@ -32,9 +34,9 @@ private monorepo packages (`aries-datalake-plane`, etc.) installed.
32
34
  export ARIES_HOME=/tmp/aries-test-$$
33
35
 
34
36
  # Free ports by default (or pass --control-port / --plane-port)
35
- aries datalake dev up
37
+ aries datalake up
36
38
  aries datalake list
37
- aries datalake dev down
39
+ aries datalake down
38
40
  ```
39
41
 
40
42
  ### Programmatic client (no shell-out per request)
@@ -45,7 +47,7 @@ import {
45
47
  RestPayloadsClient,
46
48
  } from '@ariestools/cli/datalake'
47
49
 
48
- // After `aries datalake dev up` (or with baseUrl + authToken options)
50
+ // After `aries datalake up` (or with baseUrl + authToken options)
49
51
  const control = createDatalakeClient()
50
52
  const lakes = await control.list()
51
53
  // …
@@ -56,17 +58,22 @@ Private workspace packages stay private; runtime daemons ship under
56
58
 
57
59
  ## Top-level commands
58
60
 
61
+ The reference includes public `aries` commands and internal `ariesi` commands.
62
+ Internal commands require the private CLI build and an internal license tier;
63
+ the public installation cannot run them.
64
+
59
65
  ```text
60
66
  aries ai AI client tools
61
- aries bank Manage bank witness settings
67
+ ariesi bank Manage bank witness settings
62
68
  aries chain Run a local published-chain S3/REST server
63
- aries clamp Run commands inside a permission-controlled sandbox
64
69
  aries datalake Provision and manage on-demand XL1 datalakes
65
- aries hash [file] Generate a hash (sha256, xyo, fuzzy, perceptual)
70
+ ariesi gate Query the Gate exchange API (internal build)
71
+ ariesi hash [file] Generate a hash (sha256, xyo, fuzzy, perceptual)
66
72
  aries info Display version and environment info
67
- aries npmjs Inspect and lint npmjs org packages
68
- aries pentair Manage Pentair ScreenLogic systems
73
+ ariesi npmjs Inspect and lint npmjs org packages
74
+ ariesi pentair Manage Pentair ScreenLogic systems
69
75
  ariesi plex Inspect and manage Plex Media Server (internal build)
76
+ aries secrets Secrets foundation, static AI instructions, and the human-only vault path
70
77
  aries wallet XL1 wallet — seed phrases, accounts, transactions, contacts
71
78
  aries witness Observe data and produce XYO-compliant payloads
72
79
  aries xyo XYO protocol utilities
@@ -79,11 +86,561 @@ Available on every command:
79
86
  | Option | Description |
80
87
  |---|---|
81
88
  | `-v, --verbose` | Enable verbose output |
89
+ | `--help-ai` | Print static AI-oriented help for a registered command path |
82
90
  | `--version` | Show version number |
83
91
  | `--help` | Show help |
84
92
 
85
93
  ---
86
94
 
95
+ ## `aries secrets` — Secrets foundation and the human-only vault path
96
+
97
+ The current implementation provides versioned, static discovery and status,
98
+ the read-only inventory listing, local bastion profile and provider
99
+ configuration, and eight human-only commands, `init`, `create`, `reveal`,
100
+ `set`, `generate`, `delete`, `list`, and `describe`, each documented in the
101
+ next sections:
102
+
103
+ ```sh
104
+ aries secrets status
105
+ aries secrets status --json
106
+ aries secrets --help-ai
107
+ aries secrets --help-ai --json
108
+ aries secrets init # human-only; no --json form
109
+ aries secrets create --name "Deploy token" --input-fd 3 3<file # human-only; value from fd 3, else the prompt
110
+ aries secrets reveal --secret-id sec_… # human-only; the value, once, to the terminal
111
+ aries secrets set --secret-id sec_… --expected-version <v> --input-fd 3 3<file # human-only; compare-and-swap replace
112
+ aries secrets generate --name "Signing key" --kind random-bytes --length 64 # human-only; drawn in-process, never printed
113
+ aries secrets delete --secret-id sec_… --expected-version <v> # human-only; compare-and-swap delete, not recoverable
114
+ aries secrets list # human-only; metadata only
115
+ aries secrets describe --secret-id sec_… # human-only; metadata only
116
+ ```
117
+
118
+ `status` reports the implementation phase (`human-only-storage`) plus three
119
+ live observations: `bastionConfigured` is true when the bastion profile store
120
+ holds at least one profile and `data.bastionStore` names the store path and
121
+ its state (`configured`, `empty`, or `unreadable` — a refused or corrupt store
122
+ is reported, never thrown); `data.providers` carries the negotiated state of
123
+ every provider entry on the selected profile; and `data.humanOnlyPath` states
124
+ whether the human-only path is available on this platform. `data.state` is
125
+ `not-initialized` until a vault exists on the selected profile and
126
+ `human-only-storage` once one does. There is no `initialized` state and no
127
+ authenticated operator: `init` performs no operator or recovery bootstrap, so
128
+ the realm stays provisional and nothing accepts a grant.
129
+
130
+ `aries --help-ai [--json]` discovers registered AI-help subtrees. AI help is
131
+ embedded package content and runs before `.env`, settings, licensing, provider,
132
+ or broker initialization. Its manifest marks future commands as `planned`, the
133
+ human-only commands as `human-only` (risk `human-only`, no `--json` option),
134
+ and carries a `humanOnlyPath` block: `brokered: false`, `humanAttested:
135
+ true`, the eight conditions every human-only command enforces, the platforms
136
+ on which they can be satisfied (`darwin`, `linux`), `unlock:
137
+ 'per-invocation'`, and `lease: 'not-available'`.
138
+
139
+ Agent-operational secret access is not implemented. This foundation cannot
140
+ connect to a bastion, create AI identities, authenticate with JWTs, manage
141
+ grants or approvals, or broker a secret to any program. The inventory listing
142
+ below observes presence metadata only: it initializes no provider and reads no
143
+ secret value. The bastion profile and provider commands record non-secret
144
+ configuration only: they contact no bastion and create no socket or vault.
145
+ The one path that touches a vault is human-only and unbrokered, described
146
+ under `aries secrets init`, `aries secrets create`, `aries secrets reveal`,
147
+ and the `set`, `generate`, `delete`, `list`, and `describe` sections below.
148
+
149
+ ### The refusal contract
150
+
151
+ All eight human-only commands — `init`, `create`, `reveal`, `set`,
152
+ `generate`, `delete`, `list`, and `describe` — refuse unless standard input
153
+ and standard output are both interactive terminals, the platform provides
154
+ `/dev/tty`, `stty`, and a process-ancestry probe (an allow-list, so `win32`
155
+ and any unknown platform are refused outright), no AI agent host is detected
156
+ through a documented environment signal or an agent binary in the process
157
+ ancestry (an ambiguous probe fails closed), and argv carries no
158
+ structured-output option (`--json`, `--no-json`) and no delivery option
159
+ (`--delivery`, `--destination`, `--output`, `--out`, `--out-file`, `--fd`,
160
+ `--env`, `--clipboard`, `--temp-file`, `--to`) anywhere — including before
161
+ the `secrets` word. A secret value still has two ways in, both of them input
162
+ channels rather than delivery: the masked prompt on the attached terminal,
163
+ and `--input-fd <fd>` on `create` and `set`, which reads the value from a
164
+ descriptor the caller opened. Every refusal is `HUMAN_CHANNEL_REQUIRED`
165
+ (exit 2) with a fixed message on standard error that never reflects an argv
166
+ value and nothing at all on standard output, and the two kinds differ in
167
+ what they record: a refusal by the channel gate — a pipe, a non-terminal, an
168
+ unsupported platform, a detected or ambiguous agent host — is recorded as a
169
+ human-attested `refused` audit event, while an argv refusal is caught by the
170
+ preflight before any store is opened and writes no audit event at all. The
171
+ gate conditions are evaluated in the order above, so a piped or captured
172
+ caller is refused before any `ps` probe runs. Passing the gate refuses the
173
+ non-interactive case; it is not proof of a human, because a pseudo-terminal
174
+ remains agent-drivable.
175
+
176
+ No MCP tool and no GUI reaches this path. The `aries mcp` servers register no
177
+ tool that names a secret and import nothing from the secrets action tree; the
178
+ Electron shell and the dashboard import nothing from that tree either — not
179
+ the human-only modules, not the barrel that re-exports them, not the provider
180
+ seam, and not the encrypted-file vault package — and neither declares
181
+ `@ariestools/cli-lib` as a dependency (the dashboard reads only
182
+ `secrets inventory list --json` by running the packed CLI, which reads no
183
+ value). The vault package itself is not a GUI or IPC API: the only consumers
184
+ of its unlock, create, re-key, and record primitives are the human-only
185
+ command modules that handle plaintext and the provider seam they load it
186
+ through. There is no socket, no daemon, and no lease — a command unlocks the
187
+ vault for that invocation and locks it again before it returns.
188
+
189
+ ### Storage layout under `~/.aries/secrets`
190
+
191
+ `ARIES_HOME` relocates `~/.aries` in full. Everything the CLI writes below it
192
+ is created `0600` inside `0700` directories. The three stores it owns —
193
+ `config/bastions.json`, `realms/<realmId>/secrets-index.json`, and
194
+ `audit/human-attested.jsonl` — are also checked when they are read: a group
195
+ or other permission bit, a foreign owner, a symlink, a non-regular file, or
196
+ an oversized one fails the command and names the `chmod` or `chown` fix
197
+ rather than repairing it. The vault files are written the same way and are
198
+ refused on a symlink, a non-regular file, or an oversized one, and their
199
+ directory is created `0700`. Two rows below are operator-authored and are
200
+ never written, moved, or permission-checked by the CLI: `inventory.json`,
201
+ read as declarations only, and `realms/<realmId>/denies.json`, which refuses
202
+ every secret-touching operation in this milestone by existing at all.
203
+
204
+ | Path | Contents |
205
+ | --- | --- |
206
+ | `config/bastions.json` | The bastion profile envelope: realms, bastions, endpoints, provider entries, and the active selection. Non-secret. `ARIES_SECRETS_BASTIONS` redirects it (absolute path). |
207
+ | `vaults/<providerId>/vault.json` | The encrypted-file vault header, format v1: plaintext parameters only — the vault ID, PBKDF2-SHA256 at 100,000 iterations, AES-256-GCM with a 12-byte IV, and the creation time. |
208
+ | `vaults/<providerId>/vault-password.json` | The password verifier: the PBKDF2 salt and a SHA-256 hash of the derived key. Never the password. |
209
+ | `vaults/<providerId>/records.json` | Every secret, as ciphertext with its authenticated metadata. The display name lives inside the encrypted body. |
210
+ | `realms/<realmId>/secrets-index.json` | The routing index: secret ID → `{ providerId, digest }`, and nothing else. |
211
+ | `realms/<realmId>/denies.json` | Deny records, consulted before every secret-touching operation. Nothing in this milestone writes it. |
212
+ | `audit/human-attested.jsonl` | The hash-chained, append-only human-attested audit log: one event per line, no secret value and no grant evidence. |
213
+ | `inventory.json` | The machine-wide managed-secret declarations, written by the operator and only read here. Presence metadata only. `ARIES_SECRETS_INVENTORY` redirects it. |
214
+
215
+ `broker.sock` is recorded on a local profile as the endpoint the future broker
216
+ would use; no socket is created, bound, or connected to in this build.
217
+
218
+ ## `aries secrets inventory` — Managed secrets visibility
219
+
220
+ ```sh
221
+ aries secrets inventory list
222
+ aries secrets inventory list --repo owner/myapp
223
+ aries secrets inventory list --source env --env-file ./deploy/.env --json
224
+ ```
225
+
226
+ `secrets` is always registered at the free tier, so every example works
227
+ unchanged in the internal `ariesi` build.
228
+
229
+ Managed secrets are declared in one schema at two scopes, each a JSON file
230
+ with a root-level `inventory` list:
231
+
232
+ - `~/.aries/secrets/inventory.json` (override with `ARIES_SECRETS_INVENTORY`;
233
+ `ARIES_HOME` relocates `~/.aries`) — machine-wide entries, each optionally
234
+ naming its `repos`. The legacy `~/.config/aries/secrets.json` is still read
235
+ when the new file is absent, with a notice on stderr and
236
+ `data.legacyInventoryPath` under `--json`; move it to the new path.
237
+ - `<repo>/.config/aries/secrets.json` — entries needed by that repository's
238
+ agents. These files are safe to commit: the closed entry shape
239
+ (`service`, `account`, optional `label`, `env`, and — global only — `repos`)
240
+ structurally rejects value-bearing fields, so a committed file can never
241
+ become a secret store.
242
+
243
+ ```json
244
+ {
245
+ "version": 1,
246
+ "inventory": [
247
+ {
248
+ "account": "ci-token",
249
+ "env": "GITHUB_TOKEN",
250
+ "label": "GitHub token",
251
+ "service": "github"
252
+ }
253
+ ]
254
+ }
255
+ ```
256
+
257
+ Presence is metadata only and never returns a value: on macOS the default
258
+ `auto` source probes the Keychain by `(service, account)` existence without
259
+ requesting the password; elsewhere (or with `--source env`) presence means the
260
+ mapped `env` variable is set and non-empty after `.env` loading. Repositories
261
+ are discovered below `ARIES_CODE_ROOT` (default `~/GitHub`). The `--json`
262
+ envelope follows the secrets result schema and contains identifiers and
263
+ presence booleans only — suitable as the dashboard panel's data source.
264
+
265
+ ## `aries secrets bastion` — Bastion profiles
266
+
267
+ ```sh
268
+ aries secrets bastion add --name "Laptop bastion"
269
+ aries secrets bastion list
270
+ aries secrets bastion show # active or sole profile
271
+ aries secrets bastion show "Laptop bastion" # by display name
272
+ aries secrets bastion show --bastion bpf_… # by profile ID
273
+ aries secrets bastion use "Laptop bastion"
274
+ aries secrets bastion remove "Laptop bastion"
275
+ aries secrets bastion list --json
276
+ ```
277
+
278
+ A bastion profile is a closed, versioned, non-secret record naming a realm,
279
+ a bastion, its endpoint, and its providers; it holds no password, token, or
280
+ key material. Profiles live in one envelope at
281
+ `~/.aries/secrets/config/bastions.json` (`ARIES_HOME` relocates `~/.aries`;
282
+ `ARIES_SECRETS_BASTIONS` redirects the file for tests and must be absolute),
283
+ written `0600` inside a `0700` directory. A file or directory with group or
284
+ other permission bits, a foreign owner, a file over 256 KiB, or an unparseable
285
+ file fails every command with `BASTION_CONFIGURATION_INVALID` (exit 9), naming
286
+ only the path and the fix. A redirected store is disclosed on stderr in human
287
+ output and as `data.bastionStore` under `--json`.
288
+
289
+ - `add --name <displayName> [--kind local-ipc]` mints the profile, realm, and
290
+ bastion identifiers, derives the audience, and records the inert socket path
291
+ `<secretsRoot>/broker.sock` without touching the filesystem. The first
292
+ profile written to an empty store becomes the active selection. Every new
293
+ profile starts with one `encrypted-file` provider entry (`encrypted-local`,
294
+ vault directory `<secretsRoot>/vaults/encrypted-local`); no vault is created.
295
+ `--kind remote` is refused with `CAPABILITY_UNSUPPORTED` until Phase 9. A
296
+ display name that collides with an existing profile ID or display name is an
297
+ error, never an overwrite.
298
+ - `list` reads the store only: it never contacts a bastion or starts a broker,
299
+ succeeds on a missing store with an empty table, and marks a `remote`
300
+ profile `unusable in this build`.
301
+ - `show [<profile>] [--bastion <profile>]` resolves by profile ID first, then
302
+ display name, else the active profile, else the sole profile; the result
303
+ names which namespace matched. With nothing selectable it fails with
304
+ `BASTION_NOT_SELECTED`. The endpoint is labeled `not used in this build`.
305
+ - `use <profile>` writes `activeProfileId` and nothing else. Selection is not
306
+ authorization; a `remote` profile is refused.
307
+ - `remove <profile>` deletes the local record and clears an active selection
308
+ that pointed at it. It revokes nothing: grants, delegations, and enrollments
309
+ at the removed realm remain in force and must be revoked at the bastion
310
+ separately, which the result states in both output forms.
311
+
312
+ `add`, `use`, and `remove` are mutating: they print the store path on stderr
313
+ for every write, and an agent must not run them without explicit operator
314
+ instruction (the AI-help manifest carries that rule). Every command takes
315
+ `--json`; the envelope follows the secrets result schema and typed failures
316
+ map to the secrets exit codes.
317
+
318
+ ---
319
+
320
+ ## `aries secrets provider` — Provider entries
321
+
322
+ ```sh
323
+ aries secrets provider list
324
+ aries secrets provider show # active or sole provider
325
+ aries secrets provider show backup # by provider ID
326
+ aries secrets provider add --id backup --kind encrypted-file
327
+ aries secrets provider add --id backup --kind encrypted-file --vault-path /srv/vaults/backup
328
+ aries secrets provider configure backup --vault-path /srv/vaults/backup
329
+ aries secrets provider use backup
330
+ aries secrets provider remove backup
331
+ aries secrets provider test # active or sole provider
332
+ aries secrets provider test backup --json
333
+ aries secrets provider list --bastion "Laptop bastion" --json
334
+ ```
335
+
336
+ A provider entry is the non-secret placement record on a bastion profile:
337
+ `providerId`, `kind`, and `configurationRef` (for `encrypted-file`, the
338
+ absolute vault directory, default `<secretsRoot>/vaults/<providerId>`). It
339
+ holds no password and no key material; the vault's own files hold those.
340
+ Every command takes the inherited `--bastion <profile>` (profile ID first,
341
+ then display name; omitted means the active profile, else the sole profile,
342
+ else `BASTION_NOT_SELECTED`) and `--json`, discloses the store as
343
+ `data.bastionStore`, and refuses a `remote` profile with
344
+ `CAPABILITY_UNSUPPORTED`.
345
+
346
+ - `add --id <providerId> --kind encrypted-file [--vault-path <dir>]` appends
347
+ one entry. The kind union is closed at the option table and again at the
348
+ record parser, so `memory` and `macos-keychain` are refused by both. A
349
+ duplicate ID is an error, never an overwrite. No vault and no directory is
350
+ created: initialization needs a password and is human-only.
351
+ - `list` prints every entry with `initialized` — whether `vault.json` exists
352
+ at `configurationRef`, a stat only, with no unlock and no header parse —
353
+ and marks the active provider.
354
+ - `show [<providerId>]` resolves by ID, else the active provider (set by
355
+ `use`), else the sole provider, and carries two fixed notes: unlock is
356
+ per-invocation with no lease, and compare-and-swap is cooperative (it holds
357
+ only among writers honoring the vault store lock).
358
+ - `configure <providerId> --vault-path <dir>` replaces `configurationRef`
359
+ only; the kind is immutable and nothing is created, moved, or unlocked.
360
+ - `remove <providerId>` deletes the entry and clears an active provider
361
+ selection that pointed at it. The vault directory is not deleted, which the
362
+ result states in both output forms. The last provider of a profile cannot
363
+ be removed; remove the profile instead.
364
+ - `use <providerId>` records `activeProviderId`, which satisfies `--provider`
365
+ on management commands only. Placement on `create` and `generate` still
366
+ requires `--provider` whenever the profile lists more than one provider;
367
+ there is no default provider.
368
+ - `test [<providerId>]` resolves like `show` and reports the adapter's
369
+ declared capabilities, `maximumSecretBytes`, a bounded health check, the
370
+ per-invocation unlock mode, and the cooperative compare-and-swap caveat.
371
+ It never prompts, unlocks, lists, or reads: the adapter is built around a
372
+ locked placeholder session, and a locked vault — the designed steady state
373
+ — succeeds with health `locked` and exit 0. Only an unavailable provider
374
+ fails (`PROVIDER_UNAVAILABLE`, exit 7): not initialized (`next.command`
375
+ names the human-only `init`), a corrupt header (reported with a redacted
376
+ diagnostic, never a path), or a health check that misses its deadline
377
+ (retryable). `status` runs the same negotiation for every provider entry
378
+ on the selected profile and reports `providerCount` and per-provider rows
379
+ under the same ok-when-locked rule; it still succeeds when every provider
380
+ is unavailable.
381
+
382
+ `--vault-path` must be absolute and is refused when equal to or inside the
383
+ resolved XL1 wallet root, or inside the directory named by `XL1_WALLET_HOME`
384
+ or `ARIES_WALLET_HOME` when either is set, so a vault never shares a
385
+ directory with a wallet store; the refusal names the rule, never the path.
386
+ `add`, `configure`, `remove`, and `use` are mutating: they print the store
387
+ path on stderr for every write, and an agent must not run them without
388
+ explicit operator instruction.
389
+
390
+ ## `aries secrets init` — Human-only vault creation
391
+
392
+ ```sh
393
+ aries secrets init # mints "Local bastion" when no profile exists
394
+ aries secrets init --name "Laptop bastion" # display name for the minted profile
395
+ aries secrets init --bastion "Laptop bastion" # an existing profile
396
+ aries secrets init --provider-id backup # another entry; added to the profile when absent
397
+ ```
398
+
399
+ `init` is the first human-only, unbrokered command. From an interactive
400
+ terminal it resolves the bastion profile (or mints a local one when none
401
+ exists), ensures an `encrypted-file` provider entry for `--provider-id`
402
+ (default `encrypted-local`, vault directory
403
+ `<secretsRoot>/vaults/<providerId>`), refuses an existing vault at that
404
+ entry's location (`VERSION_CONFLICT`, exit 8; the refusal names the rule,
405
+ never the path), prompts for the vault password twice on `/dev/tty` with
406
+ the vault's 12-character minimum, creates the vault, opens it once to confirm
407
+ it answers under that password, locks it again, and prints the profile,
408
+ provider, and vault summary. Unlock is per-invocation: no lease, session
409
+ file, or key survives the command. The password is read from the terminal
410
+ alone — never from an environment variable or a command-line argument.
411
+
412
+ `init` initializes one vault and one local profile only. It performs no
413
+ operator or recovery bootstrap: the realm stays provisional in the plan's
414
+ sense, nothing accepts a grant, and neither `status` nor `--help-ai`
415
+ describes the operator as authenticated.
416
+
417
+ Every human-only command enforces these rules, each covered by a test:
418
+
419
+ - standard input and standard output must both be interactive terminals; a
420
+ piped, redirected, or captured channel fails with `HUMAN_CHANNEL_REQUIRED`
421
+ (exit 2) on stderr and writes nothing to stdout;
422
+ - a detected AI agent host (Claude Code, Codex, Cursor, Aider, and the other
423
+ documented signals, or an agent binary in the process ancestry) is refused,
424
+ and an ambiguous probe fails closed;
425
+ - there is no `--json` and no delivery option; `--json`, `--no-json`,
426
+ `--delivery`, `--destination`, `--output`, `--fd`, `--env`, `--clipboard`,
427
+ `--temp-file`, or `--to` anywhere in argv — including before the `secrets`
428
+ word — is refused with a fixed message before yargs runs, and the refused
429
+ value is never reflected;
430
+ - the command is not mirrored as an MCP tool and is reachable over no IPC,
431
+ socket, or remote transport;
432
+ - every run, refusal, and failure appends a human-attested, unbrokered event
433
+ to `<secretsRoot>/audit/human-attested.jsonl`; an event that cannot be
434
+ written refuses the operation (`AUDIT_UNAVAILABLE`);
435
+ - the realm's deny records are consulted before a secret-touching operation
436
+ (`init` alone is exempt, because no realm exists before it mints one);
437
+ `create`, `reveal`, `set`, `generate`, `delete`, `list`, and `describe`
438
+ refuse with `POLICY_INVALID` (exit 5) when the deny set cannot be read,
439
+ which in this milestone includes any existing
440
+ `<secretsRoot>/realms/<realmId>/denies.json`, since nothing writes one yet;
441
+ - on `win32` every human-only command fails with `HUMAN_CHANNEL_REQUIRED` and
442
+ reason `platform-unsupported`, which `status` and the manifest state.
443
+
444
+ Agents must not invoke a human-only command, wrap it, or treat it as agent
445
+ access: it identifies no principal, evaluates no grant, and confers nothing
446
+ on any agent principal.
447
+
448
+ ## `aries secrets create` — Human-only secret storage
449
+
450
+ ```sh
451
+ aries secrets create --name "Deploy token" --input-fd 3 3<./token.txt # value from descriptor 3
452
+ aries secrets create --name "Deploy token" # masked prompt, typed twice
453
+ aries secrets create --name "API key" --provider backup # placement on a named entry
454
+ ```
455
+
456
+ `create` stores one secret in the operator's own vault. From an interactive
457
+ terminal it resolves the bastion profile (`--bastion`, else the active
458
+ profile, else the sole profile — it never mints one), places the secret on
459
+ `--provider` (required when the profile lists more than one provider,
460
+ implied when it lists exactly one; the active provider never satisfies
461
+ placement), consults the realm's deny records, appends the `intent` audit
462
+ event, prompts for the vault password on `/dev/tty`, collects the value,
463
+ writes the record, indexes it, locks the vault, and prints the secret ID
464
+ and version — never the value.
465
+
466
+ The value comes from exactly one of two sources. `--input-fd <n>` reads a
467
+ trusted producer's descriptor, which must be numbered 3 or higher, open,
468
+ and not a terminal: standard input is never accepted, because it may be
469
+ model-composed or captured in a transcript. The read is bounded at 65536
470
+ bytes and exactly one trailing newline is removed. Without `--input-fd`
471
+ the value is typed on the terminal, masked, and confirmed. In both cases
472
+ the record is stored as `text/plain`, and the descriptor is read before
473
+ the password prompt so a closed or reserved descriptor is refused early.
474
+ The display name (`--name`, at most 256 bytes, no control characters) is
475
+ stored inside the encrypted record body, where the vault's associated data
476
+ authenticates it.
477
+
478
+ The write spans two stores under two leases and is not atomic, so its order
479
+ is fixed: the provider record is created first, then the index entry. If
480
+ the index write fails, the record is deleted again under the same session
481
+ and the command reports the index failure with provider effect none; if
482
+ that deletion cannot be proven, the command reports
483
+ `PROVIDER_OUTCOME_UNKNOWN` (exit 7) naming the record's opaque ID, which
484
+ may then remain in the vault unindexed until it is reconciled.
485
+
486
+ ### The secret index
487
+
488
+ Each realm has one routing index at
489
+ `<secretsRoot>/realms/<realmId>/secrets-index.json`, written `0600` inside
490
+ a `0700` realm directory through the same journaled, atomic store that
491
+ holds the bastion profiles. It maps a secret ID to `{ providerId, digest }`
492
+ and nothing else: no display name, content type, byte length, or timestamp
493
+ ever lands in it, so the file discloses routing alone. The `digest` is
494
+ SHA-256 over the canonical JSON of `{ secretId, providerId, opaqueId }`,
495
+ computed at create and re-verified on every resolve; `opaqueId` equals the
496
+ secret ID by construction, because the human path mints a canonical
497
+ 22-character `sec_` token that the encrypted-file vault stores verbatim.
498
+ The digest is unkeyed: it detects an accidental edit or a truncated write,
499
+ and nothing more — any party with write access to the file can recompute
500
+ it. Phase 3 replaces it with a broker-held keyed MAC. Do not delete the
501
+ index: every secret it routes becomes unreachable by ID.
502
+
503
+ ## `aries secrets reveal` — Human-only reveal to the terminal
504
+
505
+ ```sh
506
+ aries secrets reveal --secret-id sec_… # the value, once, then a newline
507
+ aries secrets reveal --secret-id sec_… --bastion "Laptop" # an explicit profile
508
+ ```
509
+
510
+ `reveal` resolves `--secret-id` through the realm's secret index alone. A
511
+ missing entry is `INVALID_ARGUMENT` (exit 2); an entry naming a provider
512
+ the profile no longer lists, or one whose digest no longer verifies, is
513
+ `BASTION_CONFIGURATION_INVALID` (exit 9) — each fails closed, and no
514
+ provider is ever opened or searched to find a matching record. `--provider`
515
+ is refused with `INVALID_ARGUMENT` for the same reason: the index is the
516
+ only routing input, so a reference can never be redirected from the
517
+ command line. The command then consults the deny records, appends the
518
+ `intent` event, prompts for the vault password, reads the record, locks the
519
+ vault, appends the `completed` event, and writes the value exactly once to
520
+ standard output followed by one newline — nothing else is written there.
521
+ There is no other delivery: no destination, file, descriptor, environment,
522
+ clipboard, or temporary file, and no structured output. The value never
523
+ appears in an audit line or on standard error.
524
+
525
+ ## `aries secrets set` — Human-only compare-and-swap replace
526
+
527
+ ```sh
528
+ aries secrets set --secret-id sec_… --expected-version <v> --input-fd 3 3<./new.txt # value from descriptor 3
529
+ aries secrets set --secret-id sec_… --expected-version <v> # masked prompt, typed twice
530
+ ```
531
+
532
+ `set` replaces one secret's value in the operator's own vault. It resolves
533
+ `--secret-id` through the realm's secret index alone, exactly as `reveal`
534
+ does (`--provider` is refused with `INVALID_ARGUMENT`), consults the deny
535
+ records, appends the `intent` event, prompts for the vault password, collects
536
+ the new value from `--input-fd` (3 or higher; read before the password prompt)
537
+ or the masked terminal prompt with confirmation, and performs one
538
+ compare-and-swap replace: `--expected-version` must equal the record's
539
+ current version token exactly as the last `create`, `generate`, `set`, or
540
+ `describe` printed it, or the command fails with `VERSION_CONFLICT` (exit 8)
541
+ and nothing changes. The display name and content type are preserved from
542
+ the record. The reference does not change, so the index is untouched; the
543
+ command prints the new version and the superseded one, never the value. A
544
+ replace whose effect the provider cannot prove is `PROVIDER_OUTCOME_UNKNOWN`
545
+ (exit 7), naming the record and stating that the index still routes it.
546
+
547
+ ## `aries secrets generate` — Human-only in-process generation
548
+
549
+ ```sh
550
+ aries secrets generate --name "Signing key" --kind random-bytes --length 64
551
+ aries secrets generate --name "Webhook token" --kind base64url-token
552
+ aries secrets generate --name "Database password" --kind password --length 24 --provider backup
553
+ ```
554
+
555
+ `generate` draws a value in the operator's own process from Web Crypto and
556
+ stores it exactly as `create` would: placement on `--provider` (or the sole
557
+ provider), the deny consultation, the `intent` event, the password prompt,
558
+ the provider record first with the display name inside the encrypted body,
559
+ then the routing entry in the realm's index, with the same rollback when the
560
+ index write fails. It prints the secret ID, kind, size, and version — the
561
+ value is never printed, logged, audited, or returned, and exists only in the
562
+ record's ciphertext; read it back with `reveal`. `--kind` is a closed list
563
+ and `--length` counts the kind's unit inside an inclusive range:
564
+
565
+ | Kind | Stored as | `--length` | Default | Range |
566
+ | --- | --- | --- | --- | --- |
567
+ | `random-bytes` | `application/octet-stream`, raw bytes | bytes | 32 | 8–1024 |
568
+ | `base64url-token` | `text/plain` | characters | 32 | 8–512 |
569
+ | `password` | `text/plain` | characters | 32 | 8–256 |
570
+
571
+ `password` uses a fixed 70-symbol printable alphabet with the ambiguous
572
+ `0 O 1 l I` removed and no space, quote, backslash, `$`, backtick, or
573
+ redirection character; `base64url-token` uses the 64 base64url symbols. Both
574
+ are uniform: a byte that would bias the draw is rejected, never reduced. An
575
+ unlisted kind or an out-of-range length is `INVALID_ARGUMENT` (exit 2) before
576
+ anything runs.
577
+
578
+ ## `aries secrets delete` — Human-only compare-and-swap delete
579
+
580
+ ```sh
581
+ aries secrets delete --secret-id sec_… --expected-version <v>
582
+ ```
583
+
584
+ `delete` resolves `--secret-id` through the index alone, consults the deny
585
+ records, appends the `intent` event, prompts for the vault password, and
586
+ deletes the record under the `--expected-version` guard; a stale guard is
587
+ `VERSION_CONFLICT` (exit 8) and nothing changes. It then removes the routing
588
+ entry from the realm's index: the tombstone is the removal itself, so the ID
589
+ no longer resolves through any command and a later `describe` or `reveal`
590
+ stops at the index with `INVALID_ARGUMENT`. **The vault record is gone and is
591
+ not recoverable through aries**: the encrypted-file provider discards the
592
+ ciphertext and keeps no tombstone, which the receipt reports as
593
+ `unrecoverable`. The audit line records the secret ID and the fresh tombstone
594
+ version only. If the record is deleted but the index cannot be updated, the
595
+ command reports `PROVIDER_OUTCOME_UNKNOWN` (exit 7) naming both facts — the
596
+ record is gone, the index may still route the ID. Until you reconcile,
597
+ `describe` on that ID fails either at the vault with `RESOURCE_UNAVAILABLE`,
598
+ with `list` showing it as `missing`, or at the index with `INVALID_ARGUMENT`
599
+ if the entry did go after all.
600
+
601
+ ## `aries secrets list` — Human-only metadata listing
602
+
603
+ ```sh
604
+ aries secrets list # the active provider, or the sole provider
605
+ aries secrets list --provider backup # one provider entry
606
+ ```
607
+
608
+ `list` resolves the provider entry (`--provider`, else the active provider,
609
+ else the sole one), consults the deny records for the realm's catalog,
610
+ prompts for the vault password, and prints metadata alone: for every index
611
+ entry routed to that provider, the secret ID, version, incarnation, content
612
+ type, byte length, display name, and created and updated timestamps, read
613
+ from the vault's own description; an entry whose record the provider no
614
+ longer holds is shown as `missing`, one whose index digest does not verify
615
+ as `integrity-failed` (the vault is not consulted for it), and one whose
616
+ record the vault holds but will not describe as `undescribable` with the
617
+ failure code — a single unreadable record never hides the rest of the
618
+ catalog. Entries routed to other providers on the profile are counted, not
619
+ listed. The
620
+ command also enumerates the provider's own records and reports every record
621
+ the index does not route to that provider as `unindexed`, with its opaque
622
+ ID, version, byte length, and timestamps — never its value or display name.
623
+ An unindexed record is reachable by no aries command until it is reconciled,
624
+ because the index is the only routing input. No value is ever printed.
625
+
626
+ ## `aries secrets describe` — Human-only metadata for one secret
627
+
628
+ ```sh
629
+ aries secrets describe --secret-id sec_…
630
+ ```
631
+
632
+ `describe` resolves `--secret-id` through the index alone (`--provider` is
633
+ refused), consults the deny records, prompts for the vault password, and
634
+ prints the record's current version, incarnation, created and updated
635
+ timestamps, content type, byte length, and display name — never the value,
636
+ which is decrypted only to measure it and wiped inside the library. A
637
+ record the vault does not hold is `RESOURCE_UNAVAILABLE` (exit 7); an ID the
638
+ index does not route is `INVALID_ARGUMENT` (exit 2) before any vault is
639
+ opened. The printed version is the `--expected-version` for the next `set`
640
+ or `delete`.
641
+
642
+ ---
643
+
87
644
  ## `aries chain` — Local published-chain server
88
645
 
89
646
  ```sh
@@ -118,17 +675,17 @@ CA file for another HTTPS S3-compatible endpoint.
118
675
 
119
676
  ---
120
677
 
121
- ## `aries bank` — Manage bank witness settings
678
+ ## `ariesi bank` — Manage bank witness settings
122
679
 
123
680
  ```sh
124
- aries bank capitalone config get
125
- aries bank capitalone config set base-url https://api-sandbox.capitalone.com
126
- aries bank capitalone config set token-url https://api-sandbox.capitalone.com/oauth2/token
127
- aries bank capitalone config set client-id "$CAPITALONE_CLIENT_ID"
128
- aries bank capitalone config set client-secret "$CAPITALONE_CLIENT_SECRET"
129
- aries bank capitalone config set accounts-path /accounts
130
- aries bank capitalone config set balances-path '/accounts/{accountId}/balances'
131
- aries bank capitalone config set transactions-path '/accounts/{accountId}/transactions'
681
+ ariesi bank capitalone config get
682
+ ariesi bank capitalone config set base-url https://api-sandbox.capitalone.com
683
+ ariesi bank capitalone config set token-url https://api-sandbox.capitalone.com/oauth2/token
684
+ ariesi bank capitalone config set client-id "$CAPITALONE_CLIENT_ID"
685
+ ariesi bank capitalone config set client-secret "$CAPITALONE_CLIENT_SECRET"
686
+ ariesi bank capitalone config set accounts-path /accounts
687
+ ariesi bank capitalone config set balances-path '/accounts/{accountId}/balances'
688
+ ariesi bank capitalone config set transactions-path '/accounts/{accountId}/transactions'
132
689
  ```
133
690
 
134
691
  Capital One config is stored in `~/.aries/bank/capitalone.json` with restricted
@@ -163,15 +720,15 @@ for one run.
163
720
 
164
721
  ---
165
722
 
166
- ## `aries pentair` — Manage Pentair ScreenLogic systems
723
+ ## `ariesi pentair` — Manage Pentair ScreenLogic systems
167
724
 
168
725
  ```sh
169
- aries pentair config get
170
- aries pentair config get address
171
- aries pentair config set address 192.0.2.10
172
- aries pentair config set port 80
173
- aries pentair config set system-name "Pentair ScreenLogic"
174
- aries pentair circuit runtime set --circuit-id 6 --hours 24 --on
726
+ ariesi pentair config get
727
+ ariesi pentair config get address
728
+ ariesi pentair config set address 192.0.2.10
729
+ ariesi pentair config set port 80
730
+ ariesi pentair config set system-name "Pentair ScreenLogic"
731
+ ariesi pentair circuit runtime set --circuit-id 6 --hours 24 --on
175
732
  ```
176
733
 
177
734
  Pentair config is stored in `~/.aries/pentair/config.json` and is used by the
@@ -192,7 +749,7 @@ Pentair witness and circuit commands. `ARIES_HOME` overrides the `~/.aries` root
192
749
 
193
750
  `config get` redacts `password`; pass `--show-secrets` to print it.
194
751
 
195
- ### `aries pentair circuit runtime set`
752
+ ### `ariesi pentair circuit runtime set`
196
753
 
197
754
  Set the ScreenLogic circuit egg timer. Runtime values are sent to ScreenLogic as
198
755
  minutes; `--hours 24` becomes `1440`.
@@ -283,64 +840,93 @@ List items currently in Plex's optimizer queue. When `--library` is omitted, Ari
283
840
 
284
841
  ---
285
842
 
286
- ## `aries ai` — AI client tools
843
+ ## `ariesi gate` — Gate exchange API
287
844
 
288
- ### `aries ai detect`
845
+ The Gate command is available from the internal `ariesi` build with
846
+ `license.tier=internal`.
289
847
 
290
- Scan for installed AI clients.
848
+ A read-only client for the [Gate](https://www.gate.com) exchange REST API (v4). It calls only
849
+ unauthenticated public spot endpoints — no API key is read, and nothing is ever traded.
850
+
851
+ These options are available on every `gate` subcommand:
291
852
 
292
853
  | Option | Default | Description |
293
854
  |---|---|---|
855
+ | `--base-url <url>` | `ARIES_GATE_API_URL`, `GATE_API_URL`, or `https://api.gateio.ws/api/v4` | Gate API v4 base URL |
294
856
  | `--json` | `false` | Output as JSON |
295
857
 
296
- ### `aries ai sync`
858
+ ### `ariesi gate pair list <symbol>`
297
859
 
298
- Sync AI config between Claude Code and Codex.
860
+ List every Gate spot pair that trades `<symbol>`. The symbol matches on either side of the pair by default, so `BTC` returns both `BTC_USDT` (where it is the base) and `ETH_BTC` (where it is the quote). Pairs that are not currently tradable are hidden unless `--all` is passed.
299
861
 
300
862
  | Option | Default | Description |
301
863
  |---|---|---|
302
- | `--from <claude\|agents>` | `claude` | Source to sync from (the authority) |
864
+ | `--side any\|base\|quote` | `any` | Match the symbol only as the base or only as the quote currency |
865
+ | `--all` | `false` | Include pairs whose trade status is not `tradable` |
303
866
 
304
- ### `aries ai lint [path]`
867
+ ```sh
868
+ ariesi gate pair list XYO
869
+ ```
305
870
 
306
- Check Claude Code and Codex AI configuration drift. The folder defaults to the current directory.
871
+ ```text
872
+ XYO_ETH XYO Network / Ethereum fee 0.2%
873
+ XYO_USDT XYO Network / Tether fee 0.2%
874
+ 2 pair(s) for XYO
875
+ ```
307
876
 
308
- | Option | Default | Description |
309
- |---|---|---|
310
- | `--scope <all\|project\|home>` | `all` | Check project config, shared/custom home config, or both |
311
- | `--json` | `false` | Output as JSON |
877
+ ### `ariesi gate pair ticker <pair>`
312
878
 
313
- ---
879
+ Show the 24-hour spot ticker for a single pair — last price, change, bid/ask, high/low, and base + quote volume.
314
880
 
315
- ## `aries clamp` — Run commands inside a permission-controlled sandbox
881
+ The pair can be written as one argument (`BTC_USDT`, `BTC/USDT`, `BTC-USDT`, or a **quoted** `"BTC|USDT"` an unquoted pipe is consumed by the shell before Aries sees it), or as its two halves in separate arguments (`BTC USDT`).
316
882
 
317
883
  ```sh
318
- aries clamp run <command> [args..] # Run a command inside the clamp sandbox
319
- aries clamp policy list # List available clamp policies
320
- aries clamp policy show <name> # Show a clamp policy
321
- aries clamp policy init # Create a starter .aries-clamp.json
884
+ ariesi gate pair ticker BTC_USDT
322
885
  ```
323
886
 
324
- ### Per-command flags
887
+ ```text
888
+ BTC_USDT
889
+ last 78862
890
+ change 24h +0.04%
891
+ bid / ask 78861.9 / 78862
892
+ high / low 79235.9 / 77638.6
893
+ base volume 4902.5960069814 BTC
894
+ quote volume 384979909.02896446684 USDT
895
+ ```
325
896
 
326
- #### `aries clamp run <command> [args..]`
327
- | Positional | | Description |
897
+ ---
898
+
899
+ ## `aries ai` — AI client tools
900
+
901
+ ### `aries ai detect`
902
+
903
+ Scan for installed AI clients.
904
+
905
+ | Option | Default | Description |
328
906
  |---|---|---|
329
- | `command` | required | Command to run inside the sandbox |
330
- | `args` | `[]` | Arguments for the command |
907
+ | `--json` | `false` | Output as JSON |
908
+
909
+ ### `aries ai lint [path]`
910
+
911
+ Check Claude Code and Codex AI configuration drift. The folder defaults to the current directory.
331
912
 
332
913
  | Option | Default | Description |
333
914
  |---|---|---|
334
- | `-p, --policy <path-or-name>` | | Policy file path or name |
335
- | `--audit` | `true` | Enable audit logging |
336
- | `--dry-run` | `false` | Show what would be sandboxed without running |
915
+ | `--scope <all\|project\|home>` | `all` | Check project config, shared/custom home config, or both |
916
+ | `--json` | `false` | Output as JSON |
337
917
 
338
- #### `aries clamp policy show <name>`
339
- | Positional | | Description |
918
+ ### `aries ai skills list [tool]`
919
+
920
+ List installed skills discovered for Claude Code, Codex, and Cursor. The optional
921
+ `tool` positional accepts `claude`, `codex`, or `cursor`; omit it to inspect all
922
+ three. Cursor currently reports no skills. Detection and listing do not modify client configuration.
923
+
924
+ | Option | Default | Description |
340
925
  |---|---|---|
341
- | `name` | required | Policy name or file path |
926
+ | `--json` | `false` | Output as JSON |
342
927
 
343
- `policy list` and `policy init` take no command-specific options.
928
+ AI lint also inspects shared/custom home plugin manifests and skill files for
929
+ drift without changing them.
344
930
 
345
931
  ---
346
932
 
@@ -486,14 +1072,17 @@ aries datalake audit purge # Delete rotated audit files older than --max-age-
486
1072
  ### Local development
487
1073
 
488
1074
  ```sh
489
- aries datalake dev up # Start the local control + data plane dev server
490
- aries datalake dev down # Stop it (clears credentials)
491
- aries datalake dev status # Report whether the local dev server is running and healthy
492
- aries datalake dev logs # Print recent dev server log output
493
- aries datalake dev reset # Stop the dev server and wipe all local state
1075
+ aries datalake up # Start the local control + data plane dev server
1076
+ aries datalake down # Stop it (clears credentials)
1077
+ aries datalake status # Report whether the local dev server is running and healthy
1078
+ aries datalake logs # Print recent dev server log output
1079
+ aries datalake reset # Stop the dev server and wipe all local state
494
1080
  ```
495
1081
 
496
- #### `aries datalake dev up`
1082
+ The original `aries datalake dev up|down|status|logs|reset` spellings remain
1083
+ available for compatibility.
1084
+
1085
+ #### `aries datalake up`
497
1086
  | Option | Default | Description |
498
1087
  |---|---|---|
499
1088
  | `--control-port <n>` | free loopback port | Control-plane port |
@@ -506,7 +1095,7 @@ aries datalake dev reset # Stop the dev server and wipe all local state
506
1095
  | `--audit-rotate` | `false` | Rotate the audit log daily (writes to `<home>/dev/audit/`) |
507
1096
  | `--audit-keep-days <n>` | | Prune rotated audit files older than this many days |
508
1097
 
509
- #### `aries datalake dev logs`
1098
+ #### `aries datalake logs`
510
1099
  | Option | Default | Description |
511
1100
  |---|---|---|
512
1101
  | `-f, --follow` | `false` | Stream new lines as they arrive |
@@ -580,14 +1169,14 @@ preserving the hydrated transaction tuple and all existing signatures.
580
1169
 
581
1170
  ---
582
1171
 
583
- ## `aries hash [file]` — content hashing
1172
+ ## `ariesi hash [file]` — content hashing
584
1173
 
585
1174
  ```text
586
- aries hash [file] [options]
587
- aries hash compare <hashA> <hashB> --algorithm <name> [--json]
1175
+ ariesi hash [file] [options]
1176
+ ariesi hash compare <hashA> <hashB> --algorithm <name> [--json]
588
1177
  ```
589
1178
 
590
- Reads input from `[file]` (positional), `--string`, or piped stdin (priority order). Dispatches to one of 11 algorithms.
1179
+ Reads input from `[file]` (positional), `--string`, or piped stdin (priority order). Dispatches to one of 10 algorithms.
591
1180
 
592
1181
  ### Options
593
1182
 
@@ -606,7 +1195,6 @@ Reads input from `[file]` (positional), `--string`, or piped stdin (priority ord
606
1195
  |---|---|---|---|---|---|
607
1196
  | `sha256` | strict | bytes | 64-char hex | — | `exact-match` |
608
1197
  | `xyo` | strict | JSON | 64-char hex | — | `exact-match` |
609
- | `tlsh` | fuzzy-byte | ≥50 bytes | 70-char hex | — | `tlsh-bit-hamming` ⚠ |
610
1198
  | `nilsimsa` | fuzzy-byte | bytes | 64-char hex | — | `nilsimsa-score` |
611
1199
  | `ssdeep` | fuzzy-byte | bytes | `bs:h1:h2` | `ssdeep` | `ssdeep-levenshtein` ⚠ |
612
1200
  | `phash` | perceptual-image | image | 16-char hex (64-bit) | — | `hamming-64` |
@@ -617,7 +1205,6 @@ Reads input from `[file]` (positional), `--string`, or piped stdin (priority ord
617
1205
  | `video-frames` | video | video file | JSON `{fps, frames:[{t,hash}]}` | `ffmpeg` | `frame-sequence-<algo>@<fps>fps` |
618
1206
 
619
1207
  ⚠ — Approximations, not the canonical reference scores:
620
- - `tlsh-bit-hamming` — the upstream `tlsh` npm package only exposes hashing, not the canonical TLSH-diff algorithm. We use bit-level Hamming over the hash body as a proxy.
621
1208
  - `ssdeep-levenshtein` — block-size-aware Levenshtein, without the canonical block-size attenuation curve.
622
1209
 
623
1210
  ### External binary install
@@ -639,29 +1226,29 @@ Each algorithm probes its binary at hash time and prints clear install instructi
639
1226
 
640
1227
  ```sh
641
1228
  # Strict
642
- aries hash --string "hello"
643
- aries hash ./payload.json --algorithm xyo
1229
+ ariesi hash --string "hello"
1230
+ ariesi hash ./payload.json --algorithm xyo
644
1231
 
645
1232
  # Byte-fuzzy
646
- aries hash --algorithm tlsh ./binary.exe
647
- aries hash compare <hashA> <hashB> --algorithm tlsh
1233
+ ariesi hash --algorithm nilsimsa ./binary.exe
1234
+ ariesi hash compare <hashA> <hashB> --algorithm nilsimsa
648
1235
 
649
1236
  # Perceptual image
650
- aries hash --algorithm phash ./photo.jpg
651
- aries hash --algorithm pdq ./photo.jpg
652
- aries hash compare <hashA> <hashB> --algorithm pdq
1237
+ ariesi hash --algorithm phash ./photo.jpg
1238
+ ariesi hash --algorithm pdq ./photo.jpg
1239
+ ariesi hash compare <hashA> <hashB> --algorithm pdq
653
1240
 
654
1241
  # Audio
655
- aries hash --algorithm chromaprint ./song.mp3 > song.fp.json
656
- aries hash compare "$(cat a.fp.json)" "$(cat b.fp.json)" --algorithm chromaprint --json
1242
+ ariesi hash --algorithm chromaprint ./song.mp3 > song.fp.json
1243
+ ariesi hash compare "$(cat a.fp.json)" "$(cat b.fp.json)" --algorithm chromaprint --json
657
1244
 
658
1245
  # Video
659
- aries hash --algorithm video-frames --fps 1 ./movie.mkv > movie.fp.json
660
- aries hash --algorithm video-frames --fps 1 --frame-algorithm pdq ./movie.mkv > movie-pdq.fp.json
661
- aries hash compare "$(cat a.fp.json)" "$(cat b.fp.json)" --algorithm video-frames
1246
+ ariesi hash --algorithm video-frames --fps 1 ./movie.mkv > movie.fp.json
1247
+ ariesi hash --algorithm video-frames --fps 1 --frame-algorithm pdq ./movie.mkv > movie-pdq.fp.json
1248
+ ariesi hash compare "$(cat a.fp.json)" "$(cat b.fp.json)" --algorithm video-frames
662
1249
  ```
663
1250
 
664
- ### `aries hash compare <hashA> <hashB>`
1251
+ ### `ariesi hash compare <hashA> <hashB>`
665
1252
 
666
1253
  | Positional | | Description |
667
1254
  |---|---|---|
@@ -679,18 +1266,18 @@ The compare command needs only the two hash strings — works against hashes gen
679
1266
 
680
1267
  ---
681
1268
 
682
- ## `aries npmjs` — npmjs org package checks
1269
+ ## `ariesi npmjs` — npmjs org package checks
683
1270
 
684
1271
  ```sh
685
- aries npmjs list --org=xylabs
686
- aries npmjs lint --org=xylabs
687
- aries npmjs lint --org=xylabs --fix
688
- aries npmjs lint # all auth-visible org packages
1272
+ ariesi npmjs list --org=xylabs
1273
+ ariesi npmjs lint --org=xylabs
1274
+ ariesi npmjs lint --org=xylabs --fix
1275
+ ariesi npmjs lint # all auth-visible org packages
689
1276
  ```
690
1277
 
691
- By default, package discovery uses your current npm CLI authentication via `npm access list packages @<org> --json`, so private or restricted org packages are included when your npm account can see them. Omit `--org` to list or lint all scoped packages returned by `npm access list packages --json`, grouped by org with a per-org summary at the end. If npm auth is unavailable for a specific `--org`, `aries npmjs` falls back to public package discovery and reports `npm auth: unauthenticated` at the end of text output. Use `--public-only` with `--org` to force public discovery.
1278
+ By default, package discovery uses your current npm CLI authentication via `npm access list packages @<org> --json`, so private or restricted org packages are included when your npm account can see them. Omit `--org` to list or lint all scoped packages returned by `npm access list packages --json`, grouped by org with a per-org summary at the end. If npm auth is unavailable for a specific `--org`, `ariesi npmjs` falls back to public package discovery and reports `npm auth: unauthenticated` at the end of text output. Use `--public-only` with `--org` to force public discovery.
692
1279
 
693
- ### `aries npmjs list`
1280
+ ### `ariesi npmjs list`
694
1281
 
695
1282
  | Option | Default | Description |
696
1283
  |---|---|---|
@@ -698,7 +1285,7 @@ By default, package discovery uses your current npm CLI authentication via `npm
698
1285
  | `--public-only` | `false` | List only public packages without using `npm access` |
699
1286
  | `--json` | `false` | Output as JSON |
700
1287
 
701
- ### `aries npmjs lint`
1288
+ ### `ariesi npmjs lint`
702
1289
 
703
1290
  Find packages that are not marked deprecated but probably should be. V1 reports warning-only findings and exits `0` unless an operational error occurs.
704
1291