npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ariestools/cli

v0.3.0

Published

Aries Tools CLI - A suite of tools by Arie Trouw

Readme

@ariestools/cli

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.

Installation

npm install -g @ariestools/cli
# or
pnpm add -g @ariestools/cli

The aries bin starts Node with --disable-sigusr1 from its shebang (#!/usr/bin/env -S node --disable-sigusr1), so a SIGUSR1 from another process never opens the V8 inspector in it. npm links the bin, so the kernel applies the shebang; pnpm's shim and npm's Windows shims pass the flag themselves. env -S is in macOS, FreeBSD, and GNU coreutils 8.30 and later; a system env without it (some BusyBox builds, such as Alpine's) cannot start the bin through its shebang: run it there as node --disable-sigusr1 "$(command -v aries)" …, or install with pnpm.

Quick start

aries --help
aries info
aries secrets status
ariesi hash --algorithm pdq ./photo.jpg
ariesi plex libraries
ariesi gate pair ticker BTC_USDT
aries wallet create
aries witness url --url https://example.com
aries datalake list

Local datalake (standalone install)

The published CLI embeds the local datalake dev server. You do not need private monorepo packages (aries-datalake-plane, etc.) installed.

# Isolated state (recommended for CI / Immortalizer)
export ARIES_HOME=/tmp/aries-test-$$

# Free ports by default (or pass --control-port / --plane-port)
aries datalake up
aries datalake list
aries datalake down

Programmatic client (no shell-out per request)

The programmatic datalake client is the root export of @ariestools/datalake-cli, the package aries datalake runs. It replaces @ariestools/cli/datalake, which this package no longer exports:

import {
  createDatalakeClient,
  RestPayloadsClient,
} from '@ariestools/datalake-cli'

// After `aries datalake up` (or with baseUrl + authToken options)
const control = createDatalakeClient()
const lakes = await control.list()
// …

The same export provides startRetainedDatalake and ExactRootArchive; its README documents them. The datalake dev daemon ships beside its aries-datalake bin, and the other runtime daemons under dist/bin/daemons/ inside this package.

Top-level commands

The reference includes public aries commands and internal ariesi commands. Internal commands require the private CLI build and an internal license tier; the public installation cannot run them.

aries ai           App identities (every tier) and AI client tools (Pro)
ariesi bank         Manage bank witness settings
aries chain        Run a local published-chain S3/REST server
aries datalake     Provision and manage on-demand XL1 datalakes
ariesi gate        Query the Gate exchange API (internal build)
ariesi hash [file]  Generate a hash (sha256, xyo, fuzzy, perceptual)
aries info         Display version and environment info
ariesi npmjs        Inspect and lint npmjs org packages
ariesi pentair      Manage Pentair ScreenLogic systems
ariesi plex        Inspect and manage Plex Media Server (internal build)
aries secrets      Secrets foundation, static AI instructions, the human-only vault path, and the local broker
aries wallet       XL1 wallet — seed phrases, accounts, transactions, contacts
aries witness      Observe data and produce XYO-compliant payloads
aries xyo          XYO protocol utilities

Global options

Available on every command:

| Option | Description | |---|---| | -v, --verbose | Enable verbose output | | --help-ai | Print static AI-oriented help for a registered command path | | --version | Show version number | | --help | Show help |


aries secrets — Secrets foundation, the human-only vault path, and the local broker

The current implementation provides versioned, static discovery and status, the read-only inventory listing, local bastion profile and provider configuration, eight human-only commands, init, create, reveal, set, generate, delete, list, and describe, and the local broker under aries secrets broker, each documented in the next sections:

aries secrets status
aries secrets status --json
aries secrets --help-ai
aries secrets --help-ai --json
aries secrets init                                  # human-only; no --json form
aries secrets create --name "Deploy token" --input-fd 3 3<file   # human-only; value from fd 3, else the prompt
aries secrets reveal --secret-id sec_…              # human-only; the value, once, to the terminal
aries secrets set --secret-id sec_… --expected-version <v> --input-fd 3 3<file   # human-only; compare-and-swap replace
aries secrets generate --name "Signing key" --kind random-bytes --length 64      # human-only; drawn in-process, never printed
aries secrets delete --secret-id sec_… --expected-version <v>                    # human-only; compare-and-swap delete, not recoverable
aries secrets list                                  # human-only; metadata only
aries secrets describe --secret-id sec_…            # human-only; metadata only
aries secrets broker status                         # read-only probe of the local broker
aries secrets broker install                        # human-only; pins and registers the broker

status reports the implementation phase (app-identity) plus five live observations: bastionConfigured is true when the bastion profile store holds at least one profile and data.bastionStore names the store path and its state (configured, empty, or unreadable — a refused or corrupt store is reported, never thrown); data.providers carries the negotiated state of every provider entry on the selected profile; and data.humanOnlyPath states whether the human-only path is available on this platform; and data.broker reports, from the filesystem alone and without connecting, whether the broker is available on this platform (reasonUnavailable is platform-unsupported where it is not), whether a local broker is installed for this secrets root, its supervisor, login start, pinned profile and runtime, whether the socket is present, and its last recorded state (probed is always false); and data.identity reports, from the filesystem alone, whether the app-identity store exists and the advisory single active signer lease (see aries ai identity). data.state is not-initialized until a vault exists on the selected profile and human-only-storage once one does. There is no initialized state and no authenticated operator: init performs no operator or recovery bootstrap, so the realm stays provisional and nothing accepts a grant.

aries --help-ai [--json] discovers registered AI-help subtrees. AI help is embedded package content and runs before .env, settings, licensing, provider, or broker initialization. Its manifest marks future commands as planned, the human-only commands as human-only (risk human-only, no --json option), and carries a humanOnlyPath block: brokered: false, humanAttested: true, the eight conditions every human-only command enforces, the platforms on which they can be satisfied (darwin, linux), unlock: 'per-invocation', and lease: 'not-available'. The broker's read-only status, doctor, and logs are available; its lifecycle commands are human-only-lifecycle; and a broker block states its transport (a Unix socket with kernel-attested peer credentials on darwin and linux, win32 unavailable), that it starts only when a human installs it, and that it exposes no agent operation. App identities have a manifest of their own, aries ai identity --help-ai [--json], which the root manifest lists beside secrets.

Agent-operational secret access is not implemented. This foundation cannot connect to a remote bastion, manage grants or approvals, or broker a secret to any program. The local broker a human installs verifies proofs on its own peer-checked connections and dispatches nothing; since aries ai identity it issues a challenge only for the single active signer lease a human installed, and the only CLI commands that talk to it beyond the read-only probe are the human-only ai identity unlock, lock, verify, rotate, revoke, and recover. The inventory listing below observes presence metadata only: it initializes no provider and reads no secret value. The bastion profile and provider commands record non-secret configuration only: they contact no bastion and create no socket or vault. The one path that touches a vault is human-only and unbrokered, described under aries secrets init, aries secrets create, aries secrets reveal, and the set, generate, delete, list, and describe sections below.

The refusal contract

All eight human-only commands — init, create, reveal, set, generate, delete, list, and describe — refuse unless standard input and standard output are both interactive terminals, the platform provides /dev/tty, stty, and a process-ancestry probe (an allow-list, so win32 and any unknown platform are refused outright), the process was started with --disable-sigusr1 (the installed bin's shebang supplies it; node aries.mjs … does not), with no other Node option outside a short allowlist (--disable-warning=…, --max-old-space-size=…, --enable-source-maps) and none in NODE_OPTIONS, with no NODE_COMPILE_CACHE and no other NODE_* variable outside NODE_ENV, NODE_PATH, NODE_VERSION, NODE_AUTH_TOKEN, and Node's certificate, proxy, warning, color, and REPL settings (a poisoned compile cache runs in place of the bundle), and with no LD_*, DYLD_*, or OPENSSL_* variable set, no AI agent host is detected through a documented environment signal or an agent binary in the process ancestry (an ambiguous probe fails closed), and argv carries no structured-output option (--json, --no-json) and no delivery option (--delivery, --destination, --output, --out, --out-file, --fd, --env, --clipboard, --temp-file, --to) anywhere — including before the secrets word. A secret value still has two ways in, both of them input channels rather than delivery: the masked prompt on the attached terminal, and --input-fd <fd> on create and set, which reads the value from a descriptor the caller opened. Every refusal is HUMAN_CHANNEL_REQUIRED (exit 2) with a fixed message on standard error that never reflects an argv value and nothing at all on standard output, and the two kinds differ in what they record: a refusal by the channel gate — a pipe, a non-terminal, an unsupported platform, an unsafe launch environment, a detected or ambiguous agent host — is recorded as a human-attested refused audit event, while an argv refusal is caught by the preflight before any store is opened and writes no audit event at all. The gate conditions are evaluated in the order above, so a piped or captured caller, and a process launched without the flag or with a preload, is refused before any ps probe runs. Past the gate, a V8 inspector open in the process is refused the same way at every masked prompt, before an --input-fd read, before reveal writes the value, and before generate draws one (the broker lifecycle commands refuse it too, and broker install checks again before it commits the runtime). Passing the gate refuses the non-interactive case; it is not proof of a human, because a pseudo-terminal remains agent-drivable.

None of the eight loads a .env file. The argument preflight, which runs before any .env is read, marks them .env-free, so a .env in the working directory, the directory the package manager was invoked from, the package root, or ~/.aries sets nothing in the environment they run in: no LD_*, DYLD_*, or OPENSSL_* variable, no NODE_OPTIONS, and no wallet password. Export a variable in the shell if one of these commands needs it. The gate's own children, the ps ancestry probe and stty, run with only PATH=/usr/bin:/bin and a plain-locale LANG, whatever the environment of the command holds.

No MCP tool and no GUI reaches this path. The aries mcp servers register no tool that names a secret and import nothing from the secrets action tree; the Electron shell and the dashboard import nothing from that tree either — not the human-only modules, not the barrel that re-exports them, not the provider seam, and not the encrypted-file vault package — and neither declares @ariestools/cli-lib as a dependency (the dashboard reads only secrets inventory list --json by running the packed CLI, which reads no value). The vault package itself is not a GUI or IPC API: the only consumers of its unlock, create, re-key, and record primitives are the human-only command modules that handle plaintext and the provider seam they load it through. The vault path uses no socket, no daemon, and no lease — a command unlocks the vault for that invocation and locks it again before it returns; the local broker neither opens nor holds a vault.

Storage layout under ~/.aries/secrets

ARIES_HOME relocates ~/.aries in full. Everything the CLI writes below it is created 0600 inside 0700 directories. The three stores it owns — config/bastions.json, realms/<realmId>/secrets-index.json, and audit/human-attested.jsonl — are also checked when they are read: a group or other permission bit, a foreign owner, a symlink, a non-regular file, or an oversized one fails the command and names the chmod or chown fix rather than repairing it. The vault files are written the same way and are refused on a symlink, a non-regular file, or an oversized one, and their directory is created 0700. Three rows below are operator-authored and are never written or moved by the CLI: inventory.json, read as declarations only, and realms/<realmId>/denies.json, which refuses every secret-touching operation in this milestone by existing at all, are not permission-checked either; inventory-sources.json, which decides which repositories the inventory reads, is refused when read on a symlink, a non-regular file, a foreign owner, or a group or other permission bit, on the file or on the secrets root that holds it.

| Path | Contents | | --- | --- | | 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). | | 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. | | vaults/<providerId>/vault-password.json | The password verifier: the PBKDF2 salt and a SHA-256 hash of the derived key. Never the password. | | vaults/<providerId>/records.json | Every secret, as ciphertext with its authenticated metadata. The display name lives inside the encrypted body. | | realms/<realmId>/secrets-index.json | The routing index: secret ID → { providerId, digest }, and nothing else. | | realms/<realmId>/denies.json | Deny records, consulted before every secret-touching operation. Nothing in this milestone writes it. | | audit/human-attested.jsonl | The hash-chained, append-only human-attested audit log: one event per line, no secret value and no grant evidence. | | inventory.json | The machine-wide managed-secret declarations, written by the operator and only read here. Presence metadata only. ARIES_SECRETS_INVENTORY redirects it. | | inventory-sources.json | The operator's opt-in list of repositories whose inventories are read, as { path, repoId } pairs. Written by the operator, only read here. | | broker.sock | The local broker's socket, recorded on a local profile when it is added and bound only by a broker a human installed and started for that profile (0600, renamed into place). | | broker/ | The local broker's state: the pinned runtimes runtime/v<major>-<sha16>/, installation.json, the display-only broker.json, the instance lock broker.lock, a launchd plist under supervisor/ when not installed at login, and logs/ (broker.log and its rotations, supervisor.log). | | audit/broker-lifecycle.jsonl | The hash-chained, append-only broker lifecycle audit log, kept apart from the vault chain: install, start, stop, restart, and uninstall, and every gate refusal of a lifecycle verb. | | identities/ | The app-identity store, written only by the human-only aries ai identity verbs: identity-store.json (store ID, KDF parameters, passphrase verifier), identities.json (public records, wrapped signing keys, enrollments with their MACs, tombstones), recovery.json (wrapped recovery keys and recovery-phrase ciphertexts), and the journal .identity-transaction.json. Never a plaintext key, mnemonic, or passphrase. The broker reads identities.json only. The lease identities.lock sits beside the directory. | | audit/identity.jsonl | The hash-chained identity audit log: every gated run of an identity verb, refusals included, with its identity's public address and enrollment, never a value. Past 1 MiB a human-only run seals it: the old log is kept unchanged as audit/identity.sealed.<head>.jsonl, and the new one starts with a checkpoint carrying what the sealed one proved. | | broker/signer.json | The broker's advisory, value-free record of the single active signer lease (identity, address, enrollment, and times, or why the last lease ended). Display only: any process of this user can write it. |

A supervised broker's unit is the one file written outside the secrets root: a launchd plist installed with --at-login goes to ~/Library/LaunchAgents, and a systemd user unit to the user manager's unit directory (~/.config/systemd/user by default).

aries secrets inventory — Managed secrets visibility

aries secrets inventory list
aries secrets inventory list --repo owner/myapp
aries secrets inventory list --source env --env-file ./deploy/.env --json

secrets is always registered at the free tier, so every example works unchanged in the internal ariesi build.

Managed secrets are declared in one schema at two scopes, each a JSON file with a root-level inventory list:

  • ~/.aries/secrets/inventory.json (override with ARIES_SECRETS_INVENTORY; ARIES_HOME relocates ~/.aries) — machine-wide entries, each optionally naming its repos. The legacy ~/.config/aries/secrets.json is still read when the new file is absent and ARIES_SECRETS_INVENTORY is not set, with a notice on stderr and data.legacyInventoryPath under --json; move it to the new path.
  • <repo>/.config/aries/secrets.json — entries needed by that repository's agents, read only for repositories the operator lists in inventory-sources.json (below). These files are safe to commit: the closed entry shape (service, account, optional label, env, and — global only — repos) structurally rejects value-bearing fields, so a committed file can never become a secret store.
{
  "version": 1,
  "inventory": [
    {
      "account": "ci-token",
      "env": "GITHUB_TOKEN",
      "label": "GitHub token",
      "service": "github"
    }
  ]
}

Identifiers are ASCII-only. service and account start and end with a letter or digit and may contain @._!- in between. Each repos ID is one or more /-separated segments of [A-Za-z0-9._-] with no . or .. segment. A label may use non-ASCII letters, but it must be in Unicode NFC and must not contain control or format code points (which include bidirectional and zero-width characters), line or paragraph separators, private-use, lone surrogate, unassigned, or default-ignorable code points. There is no emoji exception: U+200D (zero-width joiner) and U+FE0F (emoji presentation selector) are rejected, so a label such as ⚠️ or a joined emoji sequence fails. An existing inventory file with such a label, including inventory.json, now fails to load; the error names the entry index to fix.

Presence is metadata only and never returns a value: on macOS the default auto source probes the Keychain by (service, account) existence without requesting the password; elsewhere (or with --source env) presence means the mapped env variable is set and non-empty after .env loading. The --json envelope follows the secrets result schema and contains identifiers and presence booleans only — suitable as the dashboard panel's data source.

The Keychain probe exists only on macOS; there is no native credential-store probe for Linux or Windows yet. Off macOS, auto falls back to environment variables, so an entry without an env mapping reports unknown, and human output says so in one stderr line; --source keychain there reports every entry unknown with reason unsupported-platform, and human output says that on stderr instead. An explicit --source env prints no such line. --json discloses how the source was chosen as data.sourceResolution: requested (auto, env, or keychain), resolved (env or keychain, the same as data.source), reason (explicit or platform-default), and keychainAvailable.

Repository inventories are read only from repositories the operator lists in ~/.aries/secrets/inventory-sources.json (under the secrets root, so ARIES_HOME relocates it). Nothing scans for repositories: a repository that is not listed contributes nothing to the default view, however it got onto the disk, and repositories the removed scan below ARIES_CODE_ROOT used to find are not read until they are listed.

{
  "version": 1,
  "repositories": [
    { "path": "/Users/me/GitHub/owner/myapp", "repoId": "owner/myapp" }
  ]
}

Each path is an absolute directory and each repoId a valid repos ID (above). The shape is closed, holds at most 200 repositories, and repeats no path or repoId. A listed repository's rows are attributed to its repoId, never to its directory name. The CLI never writes this file and has no command for it; the operator writes it, like inventory.json. A symbolic link or anything but a regular file is refused with an instruction to replace it with a regular file the operator owns. A file owned by another user, or carrying any group or other permission bit, is refused with the chown or chmod 600 fix, and so is a secrets root holding it that is owned by another user or carries any group or other permission bit (chown or chmod 700), since whoever can write that directory can replace the file. A file written with the default umask (0644 in a 0755 root) is therefore refused until chmod 700 ~/.aries/secrets && chmod 600 ~/.aries/secrets/inventory-sources.json. On Windows those checks are reported as permissionCheck: "unavailable-on-platform" rather than performed. A malformed file also fails the command. --json discloses the file as data.repositorySources (path, status: loaded or missing, and permissionCheck); when it is missing, human output of the default view prints one stderr line naming its path. A listed path that is missing or is not a directory is counted as unreadable in data.skipped.

--repo treats an argument that is a valid repository ID as a name and anything else as a path. A name is a listed repoId, else exactly <ARIES_CODE_ROOT>/<name> (default ~/GitHub), with no suffix matching; it is never resolved against the working directory, so a directory there cannot shadow the repository named. Pass a path relative to the working directory as ./dir. A path is a listed path, else an existing directory read on request. A listed repository reports data.repoTrusted: true. One that is not listed is read only because it was named (data.repoTrusted: false; human output marks the scope (unlisted)), and its env and label are repository-authored; its ID is the name, or for a path its relative path below ARIES_CODE_ROOT or its directory name outside it. An unlisted repository's ID may not be one the sources file lists for another path. data.repoTrusted is null for the default view. Presence is still probed for a key an unlisted repository declares when inventory.json does not declare it and no listed repository disputes it, so naming a directory tells the caller whether a Keychain item or mapped variable exists for a key that directory chose; no value is read.

The --repo view is the default view narrowed to one repository: the requested repository is merged with the operator's file and every listed repository, and only its keys are shown, so a key another listed repository disputes is conflicting-declarations there too, with its collision. The requested repository is read strictly: when its inventory is malformed or cannot be read safely, or its listed path is missing, the command fails with the reason (the path and, for a malformed file, the entry index) and exit 1.

Each (service, account) key is listed once. When inventory.json declares a key, the operator's entry wins: its env, label, and repos are listed unchanged, a repository that also declares the key is never added to its repos, and the row carries declaredBy: "operator". When only repositories declare a key and they give it the same env and label, the row is attributed to each of them (declaredBy: "repository"). When they differ on either field, including one repository setting a field another leaves unset, no repository wins: the row has both fields null, presence unknown with reason conflicting-declarations, and no Keychain or environment probe runs for it. Every key a repository declares that the operator also declares, and every conflicting key, is reported in data.collisions (service, account, the declaring repos, the disputed fields, and resolution: operator or conflicting); human output prints one stderr line naming at most five of them. --repo applies the same rules across the same declarers, so a repository cannot change an operator entry, or win a key another listed repository disputes, in its own view either.

ARIES_CODE_ROOT and ARIES_SECRETS_ENV_FILE (the env file loaded when --env-file is not given) are read once, from the real process environment, when the CLI starts, the way the secrets commands already read ARIES_HOME, ARIES_SECRETS_INVENTORY, and ARIES_SECRETS_BASTIONS. Export them in the shell: a .env file in the working directory, in the directory the package manager was invoked from, in the package root (npm_config_local_prefix), or at ~/.aries/.env cannot set or move any of them, although the variables it sets still count toward env presence. The home directory these defaults sit under is also taken once at start, and a .env never sets HOME, USERPROFILE, HOMEDRIVE, HOMEPATH, USER, LOGNAME, or PATH, so a process started without HOME cannot have its secrets root moved by one either. Nor does a .env set the wallet's ARIES_WALLET_PASSWORD, ARIES_WALLET_NON_INTERACTIVE, XL1_WALLET_HOME, or ARIES_WALLET_HOME, or any name under a reserved prefix: ARIES_IDENTITY_*, and the preload class NODE_* (all but NODE_ENV), LD_*, DYLD_*, and OPENSSL_*, which would otherwise run a repository's code in every process Aries starts. Dropping a reserved name prints one fixed line to stderr; set NODE_OPTIONS, NODE_EXTRA_CA_CERTS and the rest in the real environment instead. A .env that is not a regular file (a FIFO, a device, or a directory) is skipped, as a malformed one is.

An unlisted --repo path whose derived ID would be invalid (a space, a non-ASCII letter, a control or bidirectional character in a directory name) is refused. A repository inventory is read only through real .config and .config/aries directories and only when secrets.json is a regular file, never a symbolic link or a FIFO. A listed repository inventory that is malformed or cannot be read that way is skipped rather than failing the listing: --json counts it in data.skipped (invalidRepositoryInventory, unreadableRepositoryInventory), and human output prints one count-only line on stderr. The counts cover every listed repository for the default view, and every listed repository other than the requested one for --repo. A malformed inventory.json still fails the command. Error messages print every character outside printable ASCII as a \uXXXX escape, except that a chmod or chown fix, and a path in a stderr notice, is printed as it is when it passes the display rule for labels, so a path with a non-ASCII letter stays runnable.

aries secrets bastion — Bastion profiles

aries secrets bastion add --name "Laptop bastion"
aries secrets bastion list
aries secrets bastion show                      # active or sole profile
aries secrets bastion show "Laptop bastion"     # by display name
aries secrets bastion show --bastion bpf_…      # by profile ID
aries secrets bastion use "Laptop bastion"
aries secrets bastion remove "Laptop bastion"
aries secrets bastion list --json

A bastion profile is a closed, versioned, non-secret record naming a realm, a bastion, its endpoint, and its providers; it holds no password, token, or key material. Profiles live in one envelope at ~/.aries/secrets/config/bastions.json (ARIES_HOME relocates ~/.aries; ARIES_SECRETS_BASTIONS redirects the file for tests and must be absolute), written 0600 inside a 0700 directory. A file or directory with group or other permission bits, a foreign owner, a file over 256 KiB, or an unparseable file fails every command with BASTION_CONFIGURATION_INVALID (exit 9), naming only the path and the fix. A redirected store is disclosed on stderr in human output and as data.bastionStore under --json.

  • add --name <displayName> [--kind local-ipc] mints the profile, realm, and bastion identifiers, derives the audience, and records the socket path <secretsRoot>/broker.sock without touching the filesystem; only a local broker a human installs for the profile ever binds it. The first profile written to an empty store becomes the active selection. Every new profile starts with one encrypted-file provider entry (encrypted-local, vault directory <secretsRoot>/vaults/encrypted-local); no vault is created. --kind remote is refused with CAPABILITY_UNSUPPORTED until Phase 9. A display name that collides with an existing profile ID or display name is an error, never an overwrite.
  • list reads the store only: it never contacts a bastion or starts a broker, succeeds on a missing store with an empty table, and marks a remote profile unusable in this build.
  • show [<profile>] [--bastion <profile>] resolves by profile ID first, then display name, else the active profile, else the sole profile; the result names which namespace matched. With nothing selectable it fails with BASTION_NOT_SELECTED. A local endpoint is labeled as served only by a local broker a human installs for that profile (probe it with secrets broker status).
  • use <profile> writes activeProfileId and nothing else. Selection is not authorization; a remote profile is refused.
  • remove <profile> deletes the local record and clears an active selection that pointed at it. It revokes nothing: grants, delegations, and enrollments at the removed realm remain in force and must be revoked at the bastion separately, which the result states in both output forms.

add, use, and remove are mutating: they print the store path on stderr for every write, and an agent must not run them without explicit operator instruction (the AI-help manifest carries that rule). Every command takes --json; the envelope follows the secrets result schema and typed failures map to the secrets exit codes.


aries secrets provider — Provider entries

aries secrets provider list
aries secrets provider show                       # active or sole provider
aries secrets provider show backup                # by provider ID
aries secrets provider add --id backup --kind encrypted-file
aries secrets provider add --id backup --kind encrypted-file --vault-path /srv/vaults/backup
aries secrets provider configure backup --vault-path /srv/vaults/backup
aries secrets provider use backup
aries secrets provider remove backup
aries secrets provider test                       # active or sole provider
aries secrets provider test backup --json
aries secrets provider list --bastion "Laptop bastion" --json

A provider entry is the non-secret placement record on a bastion profile: providerId, kind, and configurationRef (for encrypted-file, the absolute vault directory, default <secretsRoot>/vaults/<providerId>). It holds no password and no key material; the vault's own files hold those. Every command takes the inherited --bastion <profile> (profile ID first, then display name; omitted means the active profile, else the sole profile, else BASTION_NOT_SELECTED) and --json, discloses the store as data.bastionStore, and refuses a remote profile with CAPABILITY_UNSUPPORTED.

  • add --id <providerId> --kind encrypted-file [--vault-path <dir>] appends one entry. The kind union is closed at the option table and again at the record parser, so memory and macos-keychain are refused by both. A duplicate ID is an error, never an overwrite. No vault and no directory is created: initialization needs a password and is human-only.
  • list prints every entry with initialized — whether vault.json exists at configurationRef, a stat only, with no unlock and no header parse — and marks the active provider.
  • show [<providerId>] resolves by ID, else the active provider (set by use), else the sole provider, and carries two fixed notes: unlock is per-invocation with no lease, and compare-and-swap is cooperative (it holds only among writers honoring the vault store lock).
  • configure <providerId> --vault-path <dir> replaces configurationRef only; the kind is immutable and nothing is created, moved, or unlocked.
  • remove <providerId> deletes the entry and clears an active provider selection that pointed at it. The vault directory is not deleted, which the result states in both output forms. The last provider of a profile cannot be removed; remove the profile instead.
  • use <providerId> records activeProviderId, which stands in for an omitted provider selector on provider show, provider test, and the human-only list only; configure and remove take their positional identifier. Placement on create and generate still requires --provider whenever the profile lists more than one provider; there is no default provider.
  • test [<providerId>] resolves like show and reports the adapter's declared capabilities, maximumSecretBytes, a bounded health check, the per-invocation unlock mode, and the cooperative compare-and-swap caveat. It never prompts, unlocks, lists, or reads: the adapter is built around a locked placeholder session, and a locked vault — the designed steady state — succeeds with health locked and exit 0. Only an unavailable provider fails (PROVIDER_UNAVAILABLE, exit 7): not initialized (next.command names the human-only init), a corrupt header (reported with a redacted diagnostic, never a path), or a health check that misses its deadline (retryable). status runs the same negotiation for every provider entry on the selected profile and reports providerCount and per-provider rows under the same ok-when-locked rule; it still succeeds when every provider is unavailable.

--vault-path must be absolute and is refused when equal to or inside the resolved XL1 wallet root, or inside the directory named by XL1_WALLET_HOME or ARIES_WALLET_HOME when either is set, so a vault never shares a directory with a wallet store; the refusal names the rule, never the path. add, configure, remove, and use are mutating: they print the store path on stderr for every write, and an agent must not run them without explicit operator instruction.

aries secrets init — Human-only vault creation

aries secrets init                                   # mints "Local bastion" when no profile exists
aries secrets init --name "Laptop bastion"           # display name for the minted profile
aries secrets init --bastion "Laptop bastion"        # an existing profile
aries secrets init --provider-id backup              # another entry; added to the profile when absent

init is the first human-only, unbrokered command. From an interactive terminal it resolves the bastion profile (or mints a local one when none exists), ensures an encrypted-file provider entry for --provider-id (default encrypted-local, vault directory <secretsRoot>/vaults/<providerId>), refuses an existing vault at that entry's location (VERSION_CONFLICT, exit 8; the refusal names the rule, never the path), prompts for the vault password twice on /dev/tty with the vault's 12-character minimum, creates the vault, opens it once to confirm it answers under that password, locks it again, and prints the profile, provider, and vault summary. Unlock is per-invocation: no lease, session file, or key survives the command. The password is read from the terminal alone — never from an environment variable or a command-line argument.

init initializes one vault and one local profile only. It performs no operator or recovery bootstrap: the realm stays provisional in the plan's sense, nothing accepts a grant, and neither status nor --help-ai describes the operator as authenticated.

Every human-only command enforces these rules, each covered by a test:

  • standard input and standard output must both be interactive terminals; a piped, redirected, or captured channel fails with HUMAN_CHANNEL_REQUIRED (exit 2) on stderr and writes nothing to stdout;
  • a detected AI agent host (Claude Code, Codex, Cursor, Aider, and the other documented signals, or an agent binary in the process ancestry) is refused, and an ambiguous probe fails closed;
  • there is no --json and no delivery option; --json, --no-json, --delivery, --destination, --output, --out, --out-file, --fd, --env, --clipboard, --temp-file, or --to anywhere in argv — including before the secrets word — is refused with a fixed message before yargs runs, and the refused value is never reflected;
  • the command is not mirrored as an MCP tool and is reachable over no IPC, socket, or remote transport;
  • every run, channel-gate refusal, and failure past the gate appends a human-attested, unbrokered event to <secretsRoot>/audit/human-attested.jsonl; an argv refusal is caught by the preflight and appends none. An intent or completed event that cannot be written fails the command with AUDIT_UNAVAILABLE (intent is appended before the password prompt, completed after the operation and before anything reaches standard output, so a vault change already made stands); an unwritable refused or failed event is reported on stderr and the original failure stands;
  • the realm's deny records are consulted before a secret-touching operation (init alone is exempt, because no realm exists before it mints one); create, reveal, set, generate, delete, list, and describe refuse with POLICY_INVALID (exit 5) when the deny set cannot be read, which in this milestone includes any existing <secretsRoot>/realms/<realmId>/denies.json, since nothing writes one yet;
  • on win32 no human-only command can pass the channel gate: with both standard streams on a terminal it fails with HUMAN_CHANNEL_REQUIRED and reason platform-unsupported, which status and the manifest state, and a piped stream is refused earlier as stdin-not-tty or stdout-not-tty.

Agents must not invoke a human-only command, wrap it, or treat it as agent access: it identifies no principal, evaluates no grant, and confers nothing on any agent principal.

aries secrets create — Human-only secret storage

aries secrets create --name "Deploy token" --input-fd 3 3<./token.txt   # value from descriptor 3
aries secrets create --name "Deploy token"                               # masked prompt, typed twice
aries secrets create --name "API key" --provider backup                  # placement on a named entry

create stores one secret in the operator's own vault. From an interactive terminal it resolves the bastion profile (--bastion, else the active profile, else the sole profile — it never mints one), places the secret on --provider (required when the profile lists more than one provider, implied when it lists exactly one; the active provider never satisfies placement), consults the realm's deny records, appends the intent audit event, prompts for the vault password on /dev/tty, collects the value, writes the record, indexes it, locks the vault, and prints the secret ID and version — never the value.

The value comes from exactly one of two sources. --input-fd <n> reads a trusted producer's descriptor, which must be numbered 3 or higher, open, and not a terminal: standard input is never accepted, because it may be model-composed or captured in a transcript. The read is bounded at 65536 bytes and exactly one trailing newline is removed. Without --input-fd the value is typed on the terminal, masked, and confirmed. In both cases the record is stored as text/plain, and the descriptor is read before the password prompt so a closed or reserved descriptor is refused early. The display name (--name, at most 256 bytes, no control characters) is stored inside the encrypted record body, where the vault's associated data authenticates it.

The write spans two stores under two leases and is not atomic, so its order is fixed: the provider record is created first, then the index entry. If the index write fails, the record is deleted again under the same session and the command reports the index failure with provider effect none; if that deletion cannot be proven, the command reports PROVIDER_OUTCOME_UNKNOWN (exit 7) naming the record's opaque ID, which may then remain in the vault unindexed until it is reconciled.

The secret index

Each realm has one routing index at <secretsRoot>/realms/<realmId>/secrets-index.json, written 0600 inside a 0700 realm directory through the same journaled, atomic store that holds the bastion profiles. It maps a secret ID to { providerId, digest } and nothing else: no display name, content type, byte length, or timestamp ever lands in it, so the file discloses routing alone. The digest is SHA-256 over the canonical JSON of { secretId, providerId, opaqueId }, computed at create and re-verified on every resolve; opaqueId equals the secret ID by construction, because the human path mints a canonical 22-character sec_ token that the encrypted-file vault stores verbatim. The digest is unkeyed: it detects an accidental edit or a truncated write, and nothing more — any party with write access to the file can recompute it. Phase 3 replaces it with a broker-held keyed MAC. Do not delete the index: every secret it routes becomes unreachable by ID.

aries secrets reveal — Human-only reveal to the terminal

aries secrets reveal --secret-id sec_…                     # the value, once, then a newline
aries secrets reveal --secret-id sec_… --bastion "Laptop"  # an explicit profile

reveal resolves --secret-id through the realm's secret index alone. A missing entry is INVALID_ARGUMENT (exit 2); an entry naming a provider the profile no longer lists, or one whose digest no longer verifies, is BASTION_CONFIGURATION_INVALID (exit 9) — each fails closed, and no provider is ever opened or searched to find a matching record. --provider is refused with INVALID_ARGUMENT for the same reason: the index is the only routing input, so a reference can never be redirected from the command line. The command then consults the deny records, appends the intent event, prompts for the vault password, reads the record, locks the vault, appends the completed event, and writes the value exactly once to standard output followed by one newline — nothing else is written there. There is no other delivery: no destination, file, descriptor, environment, clipboard, or temporary file, and no structured output. The value never appears in an audit line or on standard error.

aries secrets set — Human-only compare-and-swap replace

aries secrets set --secret-id sec_… --expected-version <v> --input-fd 3 3<./new.txt   # value from descriptor 3
aries secrets set --secret-id sec_… --expected-version <v>                             # masked prompt, typed twice

set replaces one secret's value in the operator's own vault. It resolves --secret-id through the realm's secret index alone, exactly as reveal does (--provider is refused with INVALID_ARGUMENT), consults the deny records, appends the intent event, prompts for the vault password, collects the new value from --input-fd (3 or higher; read before the password prompt) or the masked terminal prompt with confirmation, and performs one compare-and-swap replace: --expected-version must equal the record's current version token exactly as the last create, generate, set, or describe printed it, or the command fails with VERSION_CONFLICT (exit 8) and nothing changes. The display name and content type are preserved from the record. The reference does not change, so the index is untouched; the command prints the new version and the superseded one, never the value. A replace whose effect the provider cannot prove is PROVIDER_OUTCOME_UNKNOWN (exit 7), naming the record and stating that the index still routes it.

aries secrets generate — Human-only in-process generation

aries secrets generate --name "Signing key" --kind random-bytes --length 64
aries secrets generate --name "Webhook token" --kind base64url-token
aries secrets generate --name "Database password" --kind password --length 24 --provider backup

generate draws a value in the operator's own process from Web Crypto and stores it exactly as create would: placement on --provider (or the sole provider), the deny consultation, the intent event, the password prompt, the provider record first with the display name inside the encrypted body, then the routing entry in the realm's index, with the same rollback when the index write fails. It prints the secret ID, kind, size, and version — the value is never printed, logged, audited, or returned, and exists only in the record's ciphertext; read it back with reveal. --kind is a closed list and --length counts the kind's unit inside an inclusive range:

| Kind | Stored as | --length | Default | Range | | --- | --- | --- | --- | --- | | random-bytes | application/octet-stream, raw bytes | bytes | 32 | 8–1024 | | base64url-token | text/plain | characters | 32 | 8–512 | | password | text/plain | characters | 32 | 8–256 |

password uses a fixed 70-symbol printable alphabet with the ambiguous 0 O 1 l I removed and no space, quote, backslash, $, backtick, or redirection character; base64url-token uses the 64 base64url symbols. Both are uniform: a byte that would bias the draw is rejected, never reduced. An unlisted kind or an out-of-range length is INVALID_ARGUMENT (exit 2) before anything runs.

aries secrets delete — Human-only compare-and-swap delete

aries secrets delete --secret-id sec_… --expected-version <v>

delete resolves --secret-id through the index alone, consults the deny records, appends the intent event, prompts for the vault password, and deletes the record under the --expected-version guard; a stale guard is VERSION_CONFLICT (exit 8) and nothing changes. It then removes the routing entry from the realm's index: the tombstone is the removal itself, so the ID no longer resolves through any command and a later describe or reveal stops at the index with INVALID_ARGUMENT. The vault record is gone and is not recoverable through aries: the encrypted-file provider discards the ciphertext and keeps no tombstone, which the receipt reports as unrecoverable. The audit line records the secret ID and the fresh tombstone version only. If the record is deleted but the index cannot be updated, the command reports PROVIDER_OUTCOME_UNKNOWN (exit 7) naming both facts — the record is gone, the index may still route the ID. Until you reconcile, describe on that ID fails either at the vault with RESOURCE_UNAVAILABLE, with list showing it as missing, or at the index with INVALID_ARGUMENT if the entry did go after all.

aries secrets list — Human-only metadata listing

aries secrets list                     # the active provider, or the sole provider
aries secrets list --provider backup   # one provider entry

list resolves the provider entry (--provider, else the active provider, else the sole one), consults the deny records for the realm's catalog, prompts for the vault password, and prints metadata alone: for every index entry routed to that provider, the secret ID, version, incarnation, content type, byte length, display name, and created and updated timestamps, read from the vault's own description; an entry whose record the provider no longer holds is shown as missing, one whose index digest does not verify as integrity-failed (the vault is not consulted for it), and one whose record the vault holds but will not describe as undescribable with the failure code — a single unreadable record never hides the rest of the catalog. Entries routed to other providers on the profile are counted, not listed. The command also enumerates the provider's own records and reports every record the index does not route to that provider as unindexed, with its opaque ID, version, byte length, and timestamps — never its value or display name. An unindexed record is reachable by no aries command until it is reconciled, because the index is the only routing input. No value is ever printed.

aries secrets describe — Human-only metadata for one secret

aries secrets describe --secret-id sec_…

describe resolves --secret-id through the index alone (--provider is refused), consults the deny records, prompts for the vault password, and prints the record's current version, incarnation, created and updated timestamps, content type, byte length, and display name — never the value, which is decrypted only to measure it and wiped inside the library. A record the vault does not hold is RESOURCE_UNAVAILABLE (exit 7); an ID the index does not route is INVALID_ARGUMENT (exit 2) before any vault is opened. The printed version is the --expected-version for the next set or delete.

aries secrets broker — The local broker

aries secrets broker status [--json]          # read-only: one hello and one ping
aries secrets broker doctor [--json]          # read-only: records, runtime, unit, log, one handshake
aries secrets broker logs [--lines N] [--follow] [--json]   # read-only: the broker's own log
aries secrets broker install [--profile P] [--supervisor launchd|systemd-user|none] [--at-login] [--node PATH]   # human-only
aries secrets broker start                    # human-only
aries secrets broker stop [--force]           # human-only
aries secrets broker restart                  # human-only
aries secrets broker uninstall [--purge-runtime] [--purge-logs]   # human-only
aries secrets broker doctor --deep            # human-only

The local broker is one long-running process per secrets root, on macOS and Linux only, serving one local bastion profile on <secretsRoot>/broker.sock. It accepts a connection only when the kernel reports the connecting process's uid as its own (and never uid 0), and it answers a handshake, ping, challenge issue, and proof verification. A verified proof grants nothing (dispatch: 'not-available'): the broker holds no provider, vault, or account, and at most one app-identity signing key, the single active signer lease that the human-only aries ai identity unlock or verify installs (protocol 1.1). It issues a challenge only for that lease's identity, and while the lease lasts any process running as you can authenticate as it; see aries ai identity. Only a human installs and starts it — a supervisor, when it has one, restarts it after a crash and starts it at login only with --at-login, and no settings key enables it — and help, secrets status, and inventory list never connect to it.

The read-only commands are available to agents and spawn nothing:

  • status checks the endpoint before connecting (a socket this user owns, with no group or other bit, in the owner-only secrets root; anything else is ENDPOINT_UNTRUSTED and nothing is sent), then reports the broker's pid, boot id, bundle digest, protocol, and pinned profile, and whether this CLI carries a newer broker release (updateAvailable). A stopped broker is BROKER_NOT_RUNNING (exit 7), a silent one BROKER_UNRESPONSIVE, another protocol major BROKER_PROTOCOL_MISMATCH (no fallback), and a selected profile other than the pinned one BASTION_MISMATCH.
  • doctor runs a fixed list of checks over the installation, the pinned runtime and its digest, the recorded interpreter (by path only, never executed), the supervisor unit, the recorded state and refusals, a restart loop, and one handshake, and fails with BROKER_UNHEALTHY (exit 7) when a check fails.
  • logs prints broker.log and its rotations, only lines of the broker's own event vocabulary, from an owner-only file; --follow keeps reading in this process until interrupted. Lease events are printed without the identity they name.
  • status and doctor also report, from the filesystem and without changing their outcome, the advisory signer lease from broker/signer.json and whether the pinned runtime can hold a lease (signerSupport; doctor's signer-support warning): a runtime pinned from a release older than protocol 1.1, or registered with a unit this CLI would not write, reads needs-update, fixed by secrets broker install and secrets broker restart; one with no supervisor reads unknown, and ai identity unlock and verify check it when they connect.

The lifecycle commands are human-only. They refuse unless standard input and output are both terminals, no agent host is detected, and argv carries no --json or delivery option, with HUMAN_CHANNEL_REQUIRED (exit 2) — the refusal contract of the vault commands above — and they are audited in audit/broker-lifecycle.jsonl.

  • install pins the selected (or --profile) local profile, the broker runtime this CLI carries (--from names another bundle), a stable node (--node, or the first on PATH, recorded without resolving symlinks), and a supervisor: launchd by default on macOS, systemd-user on Linux when a user manager answers, otherwise none. The runtime is copied to a content-addressed broker/runtime/v<major>-<sha16>/broker.mjs and self-tested before you confirm; that copy is the only one that ever runs, so upgrading or removing the CLI never touches a running broker. install refuses a secrets root reached through a symlink (SECRETS_ROOT_NOT_CANONICAL) and a socket path over the length policy (SOCKET_PATH_TOO_LONG) before writing anything, needs --allow-downgrade for a lower protocol major, warns before pinning an older release, and starts nothing unless --restart is given. --at-login also starts the broker at login (launchd: the plist goes to ~/Library/LaunchAgents; systemd: the unit is enabled); without it, run start after each login. Aries never enables systemd linger.
  • start re-checks the interpreter and the runtime digest, starts the broker (through its supervisor, if any), and waits for its handshake.
  • stop stops a supervised broker through its supervisor; an unsupervised one is signalled only after its handshake confirms the recorded pid (--force sends SIGKILL instead of SIGTERM).
  • restart relaunches through the supervisor and checks that the broker now runs the pinned runtime; run it after a re-pin.
  • uninstall stops the broker, unregisters its unit, removes the installation record (and, once nothing answers, the stale socket and state record), and with --purge-runtime or --purge-logs the runtimes or logs.
  • doctor --deep also re-runs the pinned runtime's self-test under the recorded interpreter and asks the supervisor about the job, read-only.

Editing or removing the pinned profile changes what the broker serves: a provider edit rebinds it (outstanding challenges are invalidated), bastion use never re-points it, and removing the pinned profile stops it until a human re-installs. The broker's exit codes, supervisor policy, and what it does and does not prove are in the repository's packages/secrets-broker-host/README.md.


aries chain — Local published-chain server

# Plain HTTP on loopback (default)
aries chain up

# Locally trusted HTTPS at https://chain.aries.test:8791 (macOS)
brew install mkcert
aries chain up --ssl auto

The server exposes the published-chain buckets at /blocks, /state, and /indexes. --ssl auto keeps the listener on 127.0.0.1, installs or reuses mkcert's local development CA, generates a certificate under ~/.aries/chain/tls, and adds an idempotent chain.aries.test entry to /etc/hosts. The first run may request administrator authorization. The CA and hosts entry remain installed after chain down; chain reset removes the generated Aries certificate files.

| Option | Default | Description | |---|---|---| | --port <number> | 8791 | Listener port | | --host <address> | 127.0.0.1 | Bind address; auto TLS requires the default | | --ssl off\|auto | off | Enable macOS automatic local HTTPS setup | | --backing memory | memory | Ephemeral storage, wiped on shutdown | | --timeout <seconds> | 10 | Startup health-check timeout |

The ready banner prints the S3_ENDPOINT used by ariesi xyo s3 .... Aries automatically selects path-style S3 addressing and trusts the generated CA for the chain.aries.test endpoint. S3_CA_BUNDLE can provide a different private CA file for another HTTPS S3-compatible endpoint.


ariesi bank — Manage bank witness settings

ariesi bank capitalone config get
ariesi bank capitalone config set base-url https://api-sandbox.capitalone.com
ariesi bank capitalone config set token-url https://api-sandbox.capitalone.com/oauth2/token
ariesi bank capitalone config set client-id "$CAPITALONE_CLIENT_ID"
ariesi bank capitalone config set client-secret "$CAPITALONE_CLIENT_SECRET"
ariesi bank capitalone config set accounts-path /accounts
ariesi bank capitalone config set balances-path '/accounts/{accountId}/balances'
ariesi bank capitalone config set transactions-path '/accounts/{accountId}/transactions'

Capital One config is stored in ~/.aries/bank/capitalone.json with restricted file permissions. ARIES_HOME overrides the ~/.aries root.

Capital One U.S. Customer Transactions is a private DevExchange product. Aries therefore requires the endpoint paths supplied by your Capital One partner docs for aries witness bank --resource accounts.

The public sandbox Retrieve Consumer Bank Products product is a deposit product catalog, not personal account data. Use aries witness bank --resource products for that API; Aries uses Capital One's documented sandbox defaults unless you override base-url or token-url.

Capital One config keys

| Key | Env var | Default | Description | |---|---|---|---| | env | ARIES_CAPITALONE_ENV | sandbox | Environment label: sandbox or production | | base-url | ARIES_CAPITALONE_BASE_URL | | Capital One API base URL | | token-url | ARIES_CAPITALONE_TOKEN_URL | | OAuth client-credentials token URL | | client-id | ARIES_CAPITALONE_CLIENT_ID | | OAuth client id | | client-secret | ARIES_CAPITALONE_CLIENT_SECRET | | OAuth client secret | | scope | ARIES_CAPITALONE_SCOPE | | Optional OAuth scope | | accounts-path | ARIES_CAPITALONE_ACCOUNTS_PATH | | Accounts endpoint path | | balances-path | ARIES_CAPITALONE_BALANCES_PATH | | Balances endpoint path; may include {accountId} | | transactions-path | ARIES_CAPITALONE_TRANSACTIONS_PATH | | Transactions endpoint path; may include {accountId} |

config get redacts client-secret; pass --show-secrets to print it. Access tokens are not persisted; pass --access-token or set ARIES_CAPITALONE_ACCESS_TOKEN for one run.


ariesi pentair — Manage Pentair ScreenLogic systems

ariesi pentair config get
ariesi pentair config get address
ariesi pentair config set address 192.0.2.10
ariesi pentair config set port 80
ariesi pentair config set system-name "Pentair ScreenLogic"
ariesi pentair circuit runtime set --circuit-id 6 --hours 24 --on

Pentair config is stored in ~/.aries/pentair/config.json and is used by the Pentair witness and circuit commands. ARIES_HOME overrides the ~/.aries root.

Config keys

| Key | Default | Description | |---|---|---| | address | | ScreenLogic adapter IP for direct connection; omit to use UDP discovery | | port | 80 | ScreenLogic adapter TCP port for direct connection | | password | SCREENLOGIC_PASSWORD | ScreenLogic password for direct connection | | system-name | Pentair ScreenLogic | ScreenLogic system name for direct connection | | response-timeout | 15000 | Milliseconds to wait for each ScreenLogic command response | | search-timeout | 5000 | Milliseconds to wait for each UDP discovery attempt | | search-attempts | 3 | Number of UDP discovery attempts before failing | | series-file | ~/.aries/pentair/intellichem.jsonl | JSONL file used for saved IntelliChem samples and graphing |

config get redacts password; pass --show-secrets to print it.

ariesi pentair circuit runtime set

Set the ScreenLogic circuit egg timer. Runtime values are sent to ScreenLogic as minutes; --hours 24 becomes 1440.

| Option | Description | |---|---| | --circuit-id <id> | Pentair circuit id to update | | --hours <n> | Runtime in hours; mutually exclusive with --minutes | | --minutes <n> | Runtime in minutes; mutually exclusive with --hours | | --on | Turn the circuit on after setting the runtime | | --json | Output as JSON |

Connection override flags are also accepted: --address, --port, --password, --system-name, --response-timeout, --search-timeout, and --search-attempts.


ariesi plex — Plex Media Server tools

The Plex command is available from the internal ariesi build with license.tier=internal.

ariesi plex libraries

List libraries configured in a Plex Media Server. By default, Aries tries the local server at http://127.0.0.1:32400 and discovers the Plex token from PLEX_TOKEN, X_PLEX_TOKEN, PLEX_AUTH_TOKEN, local Plex .LocalAdminToken, or a local Plex Preferences.xml. If local API auth still blocks access, Aries falls back to the local Plex library database and then Plex Media Scanner --list.

| Option | Default | Description | |---|---|---| | --url <url> | PLEX_URL or http://127.0.0.1:32400 | Plex server URL | | --token <token> | env/local Preferences.xml | Plex auth token | | --preferences <path> | auto-detect | Plex Preferences.xml path for token discovery | | --database <path> | auto-detect | Plex library database path for local fallback | | --no-fallback | | Disable fallback to Plex Media Scanner --list when the API is unavailable | | --json | false | Output as JSON |

ariesi plex lint [--library <name-or-id>]

Run read-only lint checks against the Plex setup. The first rule, multiple-sources, reports items that have more than one original source media copy. Plex optimized versions and files matching Plex's local extras naming conventions do not count as sources.

| Option | Default | Description | |---|---|---| | -l, --library <name-or-id> | all libraries | Plex library name or ID, e.g. Movies or 9 | | --database <path> | auto-detect | Plex library database path | | --json | false | Output as JSON |

ariesi plex items --library <name-or-id>

List top-level items in a Plex library from the local Plex database, including media resolution and optimization details.

| Option | Default | Description | |---|---|---| | -l, --library <name-or-id> | required | Plex library name or ID, e.g. Movies or 9 | | --database <path> | auto-detect | Plex library database path | | --fields <list> | media,optimizations | Comma-separated details: media, optimizations, files, all | | --limit <n> | | Maximum number of items to inspect | | --json | false | Output as JSON |

ariesi plex items optimize --tv --library <name-or-id>

Queue Plex "Optimized for TV" versions for items that do not already have one. Aries inspects the local Plex database first, skips items already marked Optimized for TV, and then queues missing