- TypeScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| extensions | ||
| src | ||
| test | ||
| .gitignore | ||
| clavister.config.example.json | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
pi-clavister
SSH-only Clavister cOS Core management for Pi. Connect to the firewall's SSH server and run commands in its native CLI. No REST requests, API credentials, local shell execution, or Linux-shell assumptions.
The extension provides a persistent interactive SSH connection and unrestricted command execution without approval prompts. Reads, writes, disruptive commands, and activate/commit can run whenever the tool is called, including in headless mode. There is no write opt-in, required reason, or recovery acknowledgement. It does not send commands on installation.
Commands can immediately change configuration or disrupt traffic. The SSH account's permissions still apply. SSH authentication, pinned host-key verification, command framing, timeouts, and output redaction remain enabled. Removing command approval does not bypass the router's own permissions or answer its interactive questions.
Install locally from Git
Repository: git.otmatas.com/pi-extensions/pi-clavister.
Prerequisites: Node.js 22+, Git, and a current Pi installation with the pi command available in your terminal. Package metadata targets Pi 0.87.1 for development. The repository is public; no Forgejo token is needed to install it. Router credentials are configured separately and are never included in the repository.
Recommended: install directly into Pi
Run in your terminal:
pi install git:https://git.otmatas.com/pi-extensions/pi-clavister.git
Pi clones the repository locally, installs its dependencies, and registers the extension in your personal Pi settings. You do not need to clone it manually or run npm install yourself. Restart Pi after installation.
Then, inside interactive Pi, configure your own SSH connection:
/clavister logins
/clavister connect
/clavister status
See Configure the SSH target for the setup wizard, host-key verification, and manual configuration. Installation itself does not connect to a firewall or send router commands.
Install for one project only
From the project directory where you want to use the extension:
pi install --local git:https://git.otmatas.com/pi-extensions/pi-clavister.git
This registers the package in that project's .pi/settings.json instead of your personal settings. Restart Pi in that project and grant project trust to load its extensions. Both installation methods run the extension on your computer; --local changes the settings scope, not where commands execute.
Alternative: clone and install your working copy
Use this if you want to inspect or edit the source locally:
git clone https://git.otmatas.com/pi-extensions/pi-clavister.git
cd pi-clavister
npm ci
npm run typecheck
npm test
pi install .
Restart Pi. The local-path installation loads this working directory directly, so keep it in place. Do not also install the Git source above, or Pi may load two copies of the extension.
For a one-session development load instead of pi install ., run from the clone:
pi -e ./extensions/clavister.ts
Update
For the direct Git installation:
pi update git:https://git.otmatas.com/pi-extensions/pi-clavister.git
For a local working-copy installation, run inside the clone:
git pull --ff-only
npm ci
Restart Pi after updating. Your SSH settings and credentials remain outside the package directory, normally in ~/.pi/agent/, and are not supplied or replaced by Git updates.
1. Enable SSH on the firewall
In the Clavister WebUI, use System → Device → Remote Management → SSH Management. Restrict access to the management interface and the source address of the computer running Pi. Use a dedicated account with the permissions needed for your commands; writes require a write-capable account. Keep Algorithms: Recommended and a working WebUI/console recovery path.
The SSH endpoint must be the Clavister itself, presenting a prompt such as:
IFLEX-300A-Ms:/>
A Linux server/jump host is not a supported substitute. For a routed management path, use a VPN or an independently configured TCP tunnel that still presents the router's SSH host key. This release does not interpret OpenSSH config, ProxyJump, shell proxy commands, or SSH-agent forwarding.
2. Configure the SSH target
Interactive setup: /clavister logins
In interactive Pi, run:
/clavister logins
The wizard asks for:
- Server IP / hostname.
- SSH port — press Enter for
22or the current port. - SSH username.
- Confirmation of the server host-key fingerprint on a new target. It reads the candidate key without authenticating; independently verify the fingerprint before accepting it. An unexpected changed key on an existing target is rejected, not silently trusted.
- Password, in a masked field. The password is never displayed or added to the chat by this extension.
- Confirmation to save the login locally.
The wizard creates the configuration if it does not exist. Run it again to replace the current login; press Enter to keep the displayed public values, then enter the password again. It manages one server, not a list of profiles. Escape/cancellation leaves the previous login unchanged. Invalid existing configuration is not overwritten: repair it or move it aside first.
After saving, run:
/clavister connect
The existing connection is disconnected; command execution remains unrestricted. Setup does not authenticate with the entered password or execute router commands; /clavister connect performs the actual login. You do not need to set a password environment variable when using this wizard.
Storage: the public connection settings go in clavister.json, normally ~/.pi/agent/clavister.json. The password is saved unencrypted in a separate generated .clavister-login-*/password.json file beside that config, referenced by auth.type: "passwordFile". New POSIX directories/files use 0700/0600; on Windows, inherited folder ACLs apply. Keep the files in your private user directory, not a shared/synced folder. Other processes/tools running as your user can read them. This is local persistence, not an OS credential vault.
The wizard removes its previous managed password file when replacing a saved login; it never deletes an arbitrary manually configured credential file. To forget a login, remove the config and its referenced generated credential file locally. Do not share either file.
The command is available only in terminal UI mode, because RPC/print mode does not provide this masked custom input. Never pass a password after the slash command or paste it into chat. Environment-variable and private-key setup remain available below for automation.
Manual configuration
Configuration precedence:
--clavister-config /absolute/path/clavister.jsonCLAVISTER_CONFIGenvironment variable<Pi agent directory>/clavister.json, normally~/.pi/agent/clavister.json
PI_CODING_AGENT_DIR is respected. Configuration is not discovered from arbitrary working-directory files. An explicit relative path is resolved against Pi's working directory; private-key paths are relative to the config file. ~ is supported.
For manual setup, copy clavister.config.example.json to that location and replace the example target/fingerprint. Files are created only when you explicitly save through /clavister logins, not when the extension loads. No credentials are read from api.bat or .env.
Password authentication through an environment variable
{
"host": "192.0.2.10",
"port": 22,
"username": "pi-audit",
"hostKeySha256": ["SHA256:REPLACE_WITH_VERIFIED_SERVER_HOST_KEY_FINGERPRINT"],
"auth": {
"type": "password",
"passwordEnv": "CLAVISTER_SSH_PASSWORD"
},
"connectTimeoutMs": 15000,
"commandTimeoutMs": 20000,
"maxResponseBytes": 262144
}
The fingerprint placeholder is intentionally invalid: replace it with a real, verified SHA256 fingerprint before connecting.
allowChanges has been removed. Existing configurations containing it remain compatible, but its value is ignored—even false no longer blocks writes. The login wizard omits it when saving. No configuration change is needed to enable commands.
Set the password in the process environment before starting Pi, without putting the literal password in shell history. PowerShell example:
$credential = Get-Credential -UserName "pi-audit" -Message "Clavister SSH credentials"
$env:CLAVISTER_SSH_PASSWORD = $credential.GetNetworkCredential().Password
pi -e ./extensions/clavister.ts
Remove-Item Env:CLAVISTER_SSH_PASSWORD
The username in the JSON remains authoritative. Environment variables are inherited by child processes and are not a vault; use a dedicated, narrowly privileged account and protect the Pi process. Do not paste passwords into the chat.
Private-key authentication
Replace the auth object:
{
"type": "privateKey",
"privateKeyPath": "~/.ssh/clavister_admin",
"passphraseEnv": "CLAVISTER_SSH_KEY_PASSPHRASE"
}
Omit passphraseEnv for an unencrypted key. Configure the matching public key on the Clavister administrative user first. Use a key algorithm supported by your device's recommended SSH configuration; do not assume that an Ed25519 client key is accepted by this firmware. Restrict key-file permissions. Inline passwords and key material in JSON are rejected.
Verify the SSH server's host key
The extension requires pinned SHA256 host-key fingerprints and never offers an insecure bypass or automatic trust-on-first-use. /clavister logins can retrieve a candidate fingerprint without authenticating, but requires explicit confirmation of independent verification before saving a new target. Obtain the expected fingerprint through an independently trusted administrative path.
To inspect a candidate key using OpenSSH utilities, substitute your actual address/port:
ssh-keyscan -p 22 -t ecdsa 192.0.2.10 > clavister-hostkeys.txt
ssh-keygen -lf clavister-hostkeys.txt -E sha256
ssh-keyscan alone does not authenticate the server. Compare the result with a trusted fingerprint before adding the SHA256:... value to hostKeySha256. Do not pin an unknown key simply because a connection failed. Multiple fingerprints are supported for independently verified host-key algorithms/HA peers or planned rotation. Never assume the HA peers have identical keys.
The fingerprints are for the server host key, not your user's login key. known_hosts is not read by this implementation.
3. Connect from Pi
/clavister status
/clavister connect
status loads configuration but opens no network connection. connect verifies the SSH host key, authenticates, opens a PTY shell, and waits for a cOS Core prompt. It does not send configuration commands.
Other commands:
/clavister logins
/clavister disconnect
/clavister reload
logins: create or replace the saved SSH login through an interactive masked-password wizard. Commands remain unrestricted after saving.disconnect: close the socket and invalidate queued work. Does not undo commands or discard pending router configuration.reload: disconnect and reread the same config file on next use. Use Pi's/reloador restart to change the configured path/flag.
Connections open lazily on the first tool call and are reused. Session shutdown/reload closes resources. A later call can reconnect after a disconnection, but the extension never replays a command automatically.
One tool: clavister_command
The extension registers exactly one model-callable tool. It connects on demand and runs one Clavister CLI command per call. SSH login and connection controls remain slash commands; use /clavister status to inspect local connection settings without contacting the router.
Parameters:
command— required, one complete cOS Core CLI command.context— optional configuration context.
There is no reason, confirmed, or approval-mode parameter. Any single-line command is sent without a permission dialog or command allowlist. Command names and printable arguments are passed to cOS Core for interpretation; this is not a local shell.
In Pi's terminal UI, each tool call displays the requested command immediately, before it finishes:
clavister_command
> help userauth
The call header also shows Context when provided. Long commands wrap to the terminal width. Sensitive fields are best-effort redacted; never put passwords in tool arguments. This display describes the requested command, not proof that it has been sent or completed. Results and errors use Pi's normal rendering.
Read-only commands
Known read-only commands run directly. For example:
{ "command": "stats" }
Other examples:
about
stats
ha
help
help connections
help -category=TYPES IPPolicy
show -changes
show -errors
show Ethernet
show IP4Address
show IPPolicy
rules
connections -show -srcip=192.0.2.20 -protocol=udp -verbose -num=50
The optional context parameter supports nested objects:
{
"command": "show IPPolicy",
"context": "IPRuleset main"
}
Every operation explicitly resets to root with cc /, optionally navigates with cc <context>, and then sends the requested command. Context and command execute as one serialized operation. Use help to verify exact object names on your firmware.
Read-only classification is only a hint for matching the returned device prompt. It does not grant or deny permission: connections -show, connections -close, and unknown commands/options all use the same unrestricted execution path.
Writes and other commands
Use the same tool with only command (and optional context):
{ "command": "set IP4Address example Address=192.0.2.20" }
Configuration changes, runtime changes, scripts, disruptive operations and lifecycle commands (activate, commit, cancel, reject) do not require confirmation. Interactive, RPC, and print/headless calls behave the same way; the command tool never accesses a confirmation UI.
Each call accepts one nonempty line of at most 2048 characters, without terminal control characters. There is no command-name or printable-argument blacklist. cc, exit, and logout are also sent when requested. A cc does not establish context for the next tool call, which always resets to root; prefer the context parameter for nested operations. Commands that close SSH, including exit/logout, may return a transport error because no final prompt arrives.
The extension does not enforce configuration ownership, take a backup, or check the effect of a command before permitting the next call. activate can apply all pending changes, including another administrator's work. activate and commit are separate tool calls; the extension does not append either automatically or manage the firmware's commit deadline.
A timeout, lost response, response limit, or recognized CLI error is still reported as an error, but does not lock subsequent commands. /clavister acknowledge has been removed. A failed response can mean a command already ran or left partial changes; the extension never retries it automatically. Later calls remain available immediately.
Boundaries and limitations
- No REST API or unsolicited router changes. Reboot, failover, activation, commit, rollback and other operations are sent when requested through the tool, without an additional approval step.
- No SCP/SFTP backup transfer, continuous packet-capture workflow, shell scripts on a Linux server, or interactive question/answer tool. The router CLI is not an OS shell.
- Pagination for all commands recognizes
--More--/[More]and cOS Core's numbered--- More (1/2) ---markers, explicit Space-to-continue/next-page instructions, and Enter/Return instructions that explicitly say next page. Recognized prompts must be stable before advancing. Identical markers on consecutive pages are consumed independently, including when a full page and its marker arrive in one packet. Progress, page count (100), bytes and elapsed time are bounded. GenericPress any key, confirmation prompts, unknown pagers and secondary login prompts are never answered automatically. - Prompt recognition supports conventional
device:/context>cOS prompts, including surrounding horizontal whitespace, colors and trailing blank lines. Read-only commands must return the connected device's name, so exampleDevice:/>prompts in help are not mistaken for completion. Firmware-specific pager formats may still require adjustment after inspecting diagnostics. - Raw CLI output is text, not an API schema. The extension detects common CLI errors, but a returned prompt is not proof that the operation achieved its intended result. Always inspect postconditions. Unknown error wording might not be recognized.
- The total per-command timeout defaults to 20 seconds; paging does not silently extend it. Responses over 256 KiB close the connection rather than consume unbounded memory. Narrow queries instead of increasing limits indiscriminately.
- Transport failures with a capture include a redacted incomplete response tail (up to 80 lines/8 KB), byte counts, elapsed/idle time, page advances, the last known prompt and structural terminal-control observations. The full bounded capture is redacted and saved as a local
diagnostics.txt; no raw transcript or OSC payload is saved. Disk-write failure still returns the tail. This remains an error—not successful command output—and commands are never retried automatically. Diagnostic files may contain sensitive network/configuration data; delete them after use. - Model output is capped at 24 KB/600 lines. Larger responses are redacted and stored in a private local temporary file, whose path is returned. No raw SSH transcript is persisted by the extension. Delete temporary output when no longer needed; Windows permissions follow inherited ACLs.
- Redaction covers configured authentication secrets, private-key blocks, and common credential fields. It is best effort, not a guarantee that every secret format is removed. Configuration, topology, usernames, IPs, MACs, and session metadata may reach the model/session log. Do not request sensitive objects unless needed. Never put credentials in tool arguments; those are logged before execution.
- This extension has no command approval gate. Pi's local tools and other extensions retain their own policies and may add separate prompts. The router account and OS/network isolation remain authorization boundaries; a read-only SSH account still cannot perform writes.
- Historical
api.bat,.research/, anddocs/API-EXPLORATION.mdartifacts are excluded from Git and the npm package. They are never read or executed by this extension.
If help userauth times out
Reload the extension with Pi's /reload and ask it to run help userauth again. The router's observed --- More (1/2) --- pager is now recognized, including other valid page counters; commands advance it with Space and still wait for the actual CLI prompt. Repeated markers on consecutive pages are also handled. If it still fails, inspect the returned incomplete response tail and the redacted diagnostic file for the exact final pager/prompt text.
pagesAdvanced: 0 with a visible unknown paging instruction suggests an unsupported pager; it does not prove that diagnosis. idleMs and byte counts distinguish a long silent wait from ongoing output at the deadline. The captured CSI/backspace/CR information can help diagnose terminal formatting without exposing a raw control-sequence transcript. Share only the relevant sanitized tail if troubleshooting further, not an entire configuration dump.
The exact cause on the live router must still be confirmed from its response. Pager handling does not bypass host-key verification or automatically retry commands.
Suggested first session
Ask Pi:
Connect to Clavister over SSH. Inspect help, firmware, HA status, and pending configuration changes. Stay read-only and do not activate anything.
For the Tailscale/static-port investigation:
Read the relevant interfaces, address objects, services and IP policies. Inspect UDP connections for the test client's LAN IP. Explain a proposed source-SAT change, but do not execute it yet.
No live SSH credentials or trusted host fingerprint were supplied during development. Tests use a local mock SSH server, not the production firewall.
Development
npm install
npm run typecheck
npm test
npm pack --dry-run
Tests cover command validation, config safety, redaction, output limits, prompt framing, pagination, timeout/cancellation, host-key mismatch, a real local SSH handshake, session reuse, context serialization, unrestricted writes in interactive/headless mode, legacy configuration compatibility, and continued execution after uncertain outcomes.
Reference: Clavister cOS Core 14 CLI reference and management guide.